--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit fa34a0731c96da043235b215b6497e9138d3352f
Parents : f4d561c
Author : Ivan <e46112d44649266d71fe2193e00a4710>
Signature : T66BB85Valid, signed by author
Date : 2026-07-22T16:18:22-05:00
chore: standardize placeholder values in various components and documentation, replacing "—" with "-" for consistency
Changes
50 files changed, 4320 insertions(+), 4320 deletions(-)
Diff
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3f6f09ea..d9ba18c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -224,7 +224,7 @@ All notable changes to this project will be documented in this file.
- **Banishment (UI)**: Blocked destinations page groups entries by identity and shows all blocked destination hashes per identity. Unblocking one unblocks the entire identity.
- **Banishment (Reticulum)**: `blackhole_identity()` is always applied when available to drop packets before LXMF delivery callbacks reach the sender, preventing "phantom deliveries" to blocked peers.
- **NomadNet file downloads**: Backtick-separated request data (e.g. ``/file/artifact`g=reticulum|r=lxmf|t=0.9.7``) is now parsed and forwarded as `var_*` request data dicts, matching upstream NomadNet behavior. Previously the raw string was passed and remote nodes could not resolve the artifact.
-- **NomadNet file downloads (cancel)**: Fixed `AttributeError` when cancelling a download — `RequestReceipt` has no `.cancel()`; we now cancel the underlying `Resource` if present, or mark the receipt `FAILED` and remove it from the link queue.
+- **NomadNet file downloads (cancel)**: Fixed `AttributeError` when cancelling a download. `RequestReceipt` has no `.cancel()`. We now cancel the underlying `Resource` if present, or mark the receipt `FAILED` and remove it from the link queue.
- **NomadNet browser (links)**: Relative `/page/` and `/file/` URLs from the Micron parser (which include backtick parameters) are now parsed correctly so they no longer show "Unsupported URL".
- **NomadNet browser (hover)**: Links with `data-destination` now show the full URL including backtick parameters in the browser hover title.
- **Docker build**: `build-frontend` stage now installs `python3` so docs generation succeeds in `node:24-alpine`.
@@ -484,7 +484,7 @@ All notable changes to this project will be documented in this file.
- **Config Editor Tool**: Added config editor tool to edit within app.
- **Repository Server Tool**: New **Tool** for the optional local file shelf to redestribute reticulum and meshchatx python wheels locally or any file you upload.
- **Dangerous Links**: The app can warn you before you open links from people you don’t know, if you happen to click on it by mistake.
-- **Message Rules**: New sieve tool for simple patterns—matching stuff can land in the right folder, quiet the notification bell, or other actions you pick.
+- **Message Rules**: New sieve tool for simple patterns. Matching stuff can land in the right folder, quiet the notification bell, or other actions you pick.
- **Chats, Images and Reactions**: Bigger chats load faster, images are grouped and sized better, styling improvements to reactions.
- **Looks and Effects**: You can make parts of the UI transparent or enable a "glass effect" look, with clear settings to control these options.
- **Simpler Internals**: The app’s settings and chat features were reorganized behind the scenes, making it easier to maintain and more stable.
@@ -681,7 +681,7 @@ All notable changes to this project will be documented in this file.
- **WebSocket (`WebSocketConnection.js`)**: Heartbeat, backoff/reconnect, and **ping/pong** handling with shared helpers in **`wsConnectionSupport.js`**.
- **Context menus (Vue)**: Shared **ContextMenuPanel**, **ContextMenuItem**, **ContextMenuDivider**, and **ContextMenuSectionLabel** (`components/contextmenu/`); styling tokens in **`style.css`**. Wired into **Contacts**, **Messages** sidebar, **ConversationViewer** message menu, **NomadNetwork** sidebar (favourites, sections, announces), and **Map** (optional **header** slot for the title row).
- **LXMF emoji reactions (UI)**: **React** section in the message context menu (Columba-aligned emoji set); inbound/outbound websocket paths merge reactions onto the target message instead of a separate row; **`mergeLxmfReactionRowsIntoMessages`** when loading history; chips below the bubble with **`title`** tooltips via **`reactionReactorLabel`** (self, selected peer, sidebar conversations, else short hash).
-- **Vue lint**: **`vue/no-reserved-keys`** — internal **`data()`** fields renamed (peer header **ResizeObserver**, Nomad Micron partial scheduling **requestAnimationFrame** handle) so ESLint passes without reserved `_` prefixes.
+- **Vue lint**: **`vue/no-reserved-keys`**: internal **`data()`** fields renamed (peer header **ResizeObserver**, Nomad Micron partial scheduling **requestAnimationFrame** handle) so ESLint passes without reserved `_` prefixes.
- **Map**: pop-out window.
- **Tools**: list-style **ToolsPage**; refreshed **Bots**, **Paper message**, **RN path**, **RN path trace**, **RNode flasher**; **About**, **Interface**, **Settings**, **Contacts**, **App** polish (loading overlays, display names, license links). Many tool and settings pages use adjusted **backgrounds and padding** for alignment with the global shell.
- **Electron**: default **context menu** for editable fields (cut/copy/paste, **spellcheck** suggestions, add to dictionary), links, and related actions; **pick file** / **pick directory** / **open path** / notifications and related **preload** helpers; loading screen refresh; CSP and **backend HTTP-only** IPC where applicable.
diff --git a/README.md b/README.md
index 934cee10..4b8b9b72 100644
--- a/README.md
+++ b/README.md
@@ -221,8 +221,8 @@ Also confirm the app is not muted in **Settings → System → Sound** and that
MeshChatX supports two levels of offline building:
-1. **Cached offline builds** — you already ran `make install` once and have `node_modules/`, `.venv/`, and local caches.
-2. **Air-gapped (zero-network) builds** — the machine has _never_ had internet. You create an offline bundle on a networked machine and transfer it.
+1. **Cached offline builds**: you already ran `make install` once and have `node_modules/`, `.venv/`, and local caches.
+2. **Air-gapped (zero-network) builds**: the machine has _never_ had internet. You create an offline bundle on a networked machine and transfer it.
### Cached Offline Builds
diff --git a/SECURITY.md b/SECURITY.md
index e9163f66..22013e7c 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -80,6 +80,6 @@ Skip one commit with `SKIP_TREE_RSM_HOOK=1`.
- **CI:** Automated pipelines (hosted on GitHub Actions) run dependency and configuration scanning (including **Trivy** and **pip-audit** on relevant paths), build checks, and security-relevant automated tests (authentication, path safety on dangerous operations, schema upgrades, backup/restore, rate limiting and access logging, and related areas). SRI integrity tests verify that external WASM/JS files match their declared hashes without regenerating the integrity manifests. CI will fail if you update these files without regenerating the integrity manifests.
- **Action pinning:** Third-party GitHub Actions are referenced with **pinned commit SHAs** in workflow definitions to reduce unexpected upgrades.
-- **Releases:** Tagged release artifacts for Linux, Windows, and macOS are produced in CI; when the pipeline also produces Android APK and/or Flatpak bundles for the tag, those binaries are included in a separate **SLSA** attestation (`meshchatx-android-flatpak-<tag>.intoto.jsonl`). **SLSA Build Level 3–style provenance** for those subjects is generated via the **generic** SLSA GitHub generator (`generator_generic_slsa3.yml` at release **v2.1.0**, with `compile-generator: true` so the builder is compiled from that tag rather than downloaded), which satisfies the **isolated builder and signed provenance** expectations for that tier; **distribution** (draft releases, mirrors) and **consumer verification** remain your operational controls, as described in upstream SLSA documentation.
+- **Releases:** Tagged release artifacts for Linux, Windows, and macOS are produced in CI. When the pipeline also produces Android APK and/or Flatpak bundles for the tag, those binaries are included in a separate **SLSA** attestation (`meshchatx-android-flatpak-<tag>.intoto.jsonl`). **SLSA Build Level 3-style provenance** for those subjects is generated via the **generic** SLSA GitHub generator (`generator_generic_slsa3.yml` at release **v2.1.0**, with `compile-generator: true` so the builder is compiled from that tag rather than downloaded), which satisfies the **isolated builder and signed provenance** expectations for that tier. **Distribution** (draft releases, mirrors) and **consumer verification** remain your operational controls, as described in upstream SLSA documentation.
- **Transparency logs:** Many Sigstore flows write to the **public Rekor** log (`https://rekor.sigstore.dev` by default). **Repository-key** `*.cosign.bundle` files next to release artifacts are built **without** a Rekor entry; with **Cosign v3+**, verify them against `cosign.pub` using `cosign verify-blob-attestation` and `--insecure-ignore-tlog=true` (signature and predicate are still checked against the public key). Private-repo or air-gapped policies may require different Sigstore settings; operators should align `COSIGN_REKOR_URL` and related variables with their own governance.
- **Cosign public key:** When repository key-based signing is used, the **public** key is published in-repo as `cosign.pub` so verifiers do not need a separate out-of-band key hunt. **Key rotation:** replace the GitHub secret holding the private key and update `cosign.pub` in the repository; older releases remain verifiable with the key that was current at build time.
diff --git a/lang/README.ru.md b/lang/README.ru.md
index 8f9b6239..efe870b2 100644
--- a/lang/README.ru.md
+++ b/lang/README.ru.md
@@ -73,7 +73,7 @@ task build
Примечания:
-- GitHub Actions собирает помеченные тегом релизы (Linux wheel/AppImage/deb/rpm, Windows, macOS, Flatpak, Android APK при теге на dev/master, SLSA, черновик релиза) в одном запуске: `.github/workflows/build-release.yml`; образ контейнера — `.github/workflows/docker.yml`. Android CI для веток и PR — `.github/workflows/android-build.yml`.
+- GitHub Actions собирает помеченные тегом релизы (Linux wheel/AppImage/deb/rpm, Windows, macOS, Flatpak, Android APK при теге на dev/master, SLSA, черновик релиза) в одном запуске: `.github/workflows/build-release.yml`. Образ контейнера: `.github/workflows/docker.yml`. Android CI для веток и PR: `.github/workflows/android-build.yml`.
- AppImage + DEB для Linux `x64` и `arm64` собираются на GitHub; RPM собирается по возможности и выкладывается, если шаг дал артефакт.
## Docker
@@ -339,7 +339,7 @@ task test
task build
```
-Цели `Makefile` — тонкие обёртки, делегирующие в `task` (те же команды, что выше):
+Цели `Makefile` - тонкие обёртки, делегирующие в `task` (те же команды, что выше):
| Команда | Делегирует в | Описание |
| -------------- | -------------- | ------------------------------------------ |
diff --git a/meshchatx.rsm b/meshchatx.rsm
index e5f7ea6a..53eae302 100644
Binary files a/meshchatx.rsm and b/meshchatx.rsm differ
diff --git a/meshchatx/src/backend/auto_propagation_manager.py b/meshchatx/src/backend/auto_propagation_manager.py
index c029e9c6..7d551df8 100644
--- a/meshchatx/src/backend/auto_propagation_manager.py
+++ b/meshchatx/src/backend/auto_propagation_manager.py
@@ -158,9 +158,9 @@ class AutoPropagationManager:
except Exception:
pass
if current_path_ok:
- # Sync is likely making progress – let it finish.
+ # Sync is likely making progress. Let it finish.
return
- # Current node is unreachable – stop the stuck sync so we can
+ # Current node is unreachable. Stop the stuck sync so we can
# look for a working alternative.
self.app.stop_propagation_node_sync(context=ctx)
# Wait briefly for the router to settle back to idle before we
diff --git a/meshchatx/src/backend/bug_report_manager.py b/meshchatx/src/backend/bug_report_manager.py
index d063dde0..746d13eb 100644
--- a/meshchatx/src/backend/bug_report_manager.py
+++ b/meshchatx/src/backend/bug_report_manager.py
@@ -545,14 +545,14 @@ class BugReportManager:
):
raise ValueError(
f"Invalid collector hash: '{dest_hex[:20]}{'...' if len(dest_hex) > 20 else ''}' "
- f"must be 32–64 hex characters (even length).",
+ f"must be 32-64 hex characters (even length).",
)
try:
dest_hash = bytes.fromhex(dest_hex)
except ValueError as exc:
raise ValueError(
f"Invalid collector hash: '{dest_hex[:20]}...' "
- f"must be 32–64 hex characters (even length).",
+ f"must be 32-64 hex characters (even length).",
) from exc
_payload, body = self._build_payload(args)
diff --git a/meshchatx/src/backend/data/licenses_backend.json b/meshchatx/src/backend/data/licenses_backend.json
index f372a171..93294833 100644
--- a/meshchatx/src/backend/data/licenses_backend.json
+++ b/meshchatx/src/backend/data/licenses_backend.json
@@ -8,7 +8,7 @@
{
"name": "aiohttp",
"version": "3.14.1",
- "author": "—",
+ "author": "-",
"license": "Apache-2.0 AND MIT"
},
{
@@ -32,7 +32,7 @@
{
"name": "audioop-lts",
"version": "0.2.2",
- "author": "—",
+ "author": "-",
"license": "PSF-2.0"
},
{
@@ -170,7 +170,7 @@
{
"name": "rns-filesync",
"version": "1.0.0",
- "author": "—",
+ "author": "-",
"license": "BSD-2-Clause"
},
{
diff --git a/meshchatx/src/backend/data/licenses_frontend.json b/meshchatx/src/backend/data/licenses_frontend.json
index 6f40a641..5fd31792 100644
--- a/meshchatx/src/backend/data/licenses_frontend.json
+++ b/meshchatx/src/backend/data/licenses_frontend.json
@@ -62,55 +62,55 @@
{
"name": "@csstools/color-helpers",
"version": "6.0.2",
- "author": "—",
+ "author": "-",
"license": "MIT-0"
},
{
"name": "@csstools/css-calc",
"version": "3.2.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@csstools/css-color-parser",
"version": "4.1.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@csstools/css-parser-algorithms",
"version": "4.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@csstools/css-syntax-patches-for-csstree",
"version": "1.1.3",
- "author": "—",
+ "author": "-",
"license": "MIT-0"
},
{
"name": "@csstools/css-tokenizer",
"version": "4.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@egjs/hammerjs",
"version": "2.0.17",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@electron-internal/extract-zip",
"version": "1.0.1",
- "author": "—",
+ "author": "-",
"license": "BSD-2-Clause"
},
{
"name": "@electron/asar",
"version": "3.4.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -140,7 +140,7 @@
{
"name": "@electron/rebuild",
"version": "4.0.4",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -182,7 +182,7 @@
{
"name": "@eslint/config-helpers",
"version": "0.4.2",
- "author": "—",
+ "author": "-",
"license": "Apache-2.0"
},
{
@@ -200,7 +200,7 @@
{
"name": "@eslint/js",
"version": "9.39.5",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -230,7 +230,7 @@
{
"name": "@gar/promise-retry",
"version": "1.0.3",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -362,7 +362,7 @@
{
"name": "@mapbox/unitbezier",
"version": "0.0.1",
- "author": "—",
+ "author": "-",
"license": "BSD-2-Clause"
},
{
@@ -410,7 +410,7 @@
{
"name": "@one-ini/wasm",
"version": "0.1.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -428,7 +428,7 @@
{
"name": "@oxc-resolver/binding-linux-x64-gnu",
"version": "11.24.2",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -464,7 +464,7 @@
{
"name": "@pkgjs/parseargs",
"version": "0.11.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -488,13 +488,13 @@
{
"name": "@rolldown/binding-linux-x64-gnu",
"version": "1.1.5",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@rolldown/pluginutils",
"version": "1.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -518,31 +518,31 @@
{
"name": "@tailwindcss/forms",
"version": "0.5.11",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@tailwindcss/node",
"version": "4.3.3",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@tailwindcss/oxide",
"version": "4.3.3",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@tailwindcss/oxide-linux-x64-gnu",
"version": "4.3.3",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@tailwindcss/vite",
"version": "4.3.3",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -560,85 +560,85 @@
{
"name": "@types/cacheable-request",
"version": "6.0.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/chai",
"version": "5.2.3",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/debug",
"version": "4.1.6",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/deep-eql",
"version": "4.0.2",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/estree",
"version": "1.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/fs-extra",
"version": "9.0.13",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/hammerjs",
"version": "2.0.46",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/http-cache-semantics",
"version": "4.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/json-schema",
"version": "7.0.15",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/keyv",
"version": "3.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/node",
"version": "0.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/rbush",
"version": "4.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/responselike",
"version": "1.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@types/trusted-types",
"version": "2.0.7",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -656,67 +656,67 @@
{
"name": "@vitest/expect",
"version": "4.1.10",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@vitest/mocker",
"version": "4.1.10",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@vitest/pretty-format",
"version": "4.1.10",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@vitest/runner",
"version": "4.1.10",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@vitest/snapshot",
"version": "4.1.10",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@vitest/spy",
"version": "4.1.10",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@vitest/ui",
"version": "4.1.10",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@vitest/utils",
"version": "4.1.10",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@volar/language-core",
"version": "2.4.28",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@volar/source-map",
"version": "2.4.28",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "@volar/typescript",
"version": "2.4.28",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -752,7 +752,7 @@
{
"name": "@vue/language-core",
"version": "3.3.7",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -800,7 +800,7 @@
{
"name": "@zarrita/storage",
"version": "0.2.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -818,13 +818,13 @@
{
"name": "acorn",
"version": "0.11.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "acorn-jsx",
"version": "5.3.2",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -842,7 +842,7 @@
{
"name": "alien-signals",
"version": "3.2.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -872,7 +872,7 @@
{
"name": "argparse",
"version": "2.0.1",
- "author": "—",
+ "author": "-",
"license": "Python-2.0"
},
{
@@ -932,7 +932,7 @@
{
"name": "balanced-match",
"version": "1.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -968,19 +968,19 @@
{
"name": "boolean",
"version": "3.0.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "brace-expansion",
"version": "1.1.12",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "buffer-from",
"version": "1.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -1040,7 +1040,7 @@
{
"name": "chalk",
"version": "4.1.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -1052,7 +1052,7 @@
{
"name": "chromium-pickle-js",
"version": "0.2.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -1124,7 +1124,7 @@
{
"name": "component-emitter",
"version": "2.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -1160,7 +1160,7 @@
{
"name": "cross-dirname",
"version": "0.1.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -1268,7 +1268,7 @@
{
"name": "dijkstrajs",
"version": "1.0.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -1292,7 +1292,7 @@
{
"name": "dotenv",
"version": "16.6.1",
- "author": "—",
+ "author": "-",
"license": "BSD-2-Clause"
},
{
@@ -1370,7 +1370,7 @@
{
"name": "electron-winstaller",
"version": "5.4.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -1514,7 +1514,7 @@
{
"name": "eslint-scope",
"version": "8.2.0",
- "author": "—",
+ "author": "-",
"license": "BSD-2-Clause"
},
{
@@ -1550,13 +1550,13 @@
{
"name": "esrecurse",
"version": "4.3.0",
- "author": "—",
+ "author": "-",
"license": "BSD-2-Clause"
},
{
"name": "estraverse",
"version": "5.1.0",
- "author": "—",
+ "author": "-",
"license": "BSD-2-Clause"
},
{
@@ -1568,7 +1568,7 @@
{
"name": "esutils",
"version": "2.0.3",
- "author": "—",
+ "author": "-",
"license": "BSD-2-Clause"
},
{
@@ -1580,7 +1580,7 @@
{
"name": "expect-type",
"version": "1.3.0",
- "author": "—",
+ "author": "-",
"license": "Apache-2.0"
},
{
@@ -1802,13 +1802,13 @@
{
"name": "got",
"version": "11.8.6",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "graceful-fs",
"version": "4.1.2",
- "author": "—",
+ "author": "-",
"license": "ISC"
},
{
@@ -1892,7 +1892,7 @@
{
"name": "immediate",
"version": "3.0.5",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -1916,7 +1916,7 @@
{
"name": "inherits",
"version": "2.0.1",
- "author": "—",
+ "author": "-",
"license": "ISC"
},
{
@@ -2018,7 +2018,7 @@
{
"name": "isbinaryfile",
"version": "4.0.8",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2066,7 +2066,7 @@
{
"name": "jiti",
"version": "2.4.2",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2096,7 +2096,7 @@
{
"name": "jsdom",
"version": "29.1.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2144,7 +2144,7 @@
{
"name": "jsqr",
"version": "1.4.0",
- "author": "—",
+ "author": "-",
"license": "Apache-2.0"
},
{
@@ -2198,19 +2198,19 @@
{
"name": "lie",
"version": "3.3.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "lightningcss",
"version": "1.32.0",
- "author": "—",
+ "author": "-",
"license": "MPL-2.0"
},
{
"name": "lightningcss-linux-x64-gnu",
"version": "1.32.0",
- "author": "—",
+ "author": "-",
"license": "MPL-2.0"
},
{
@@ -2258,7 +2258,7 @@
{
"name": "magicast",
"version": "0.5.2",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2312,7 +2312,7 @@
{
"name": "micron-parser",
"version": "0.0.0",
- "author": "—",
+ "author": "-",
"license": "Unknown"
},
{
@@ -2324,13 +2324,13 @@
{
"name": "mime-db",
"version": "1.52.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "mime-types",
"version": "2.1.35",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2420,13 +2420,13 @@
{
"name": "ms",
"version": "2.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "muggle-string",
"version": "0.4.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2444,7 +2444,7 @@
{
"name": "negotiator",
"version": "1.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2528,13 +2528,13 @@
{
"name": "ol",
"version": "10.9.0",
- "author": "—",
+ "author": "-",
"license": "BSD-2-Clause"
},
{
"name": "ol-mapbox-style",
"version": "13.4.1",
- "author": "—",
+ "author": "-",
"license": "BSD-2-Clause"
},
{
@@ -2564,7 +2564,7 @@
{
"name": "oxc-resolver",
"version": "11.24.2",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2618,13 +2618,13 @@
{
"name": "pako",
"version": "1.0.2",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "pako",
"version": "2.0.4",
- "author": "—",
+ "author": "-",
"license": "(MIT AND Zlib)"
},
{
@@ -2678,7 +2678,7 @@
{
"name": "pathe",
"version": "2.0.3",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2732,7 +2732,7 @@
{
"name": "pngjs",
"version": "5.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2744,13 +2744,13 @@
{
"name": "postcss-selector-parser",
"version": "7.1.4",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "postject",
"version": "1.0.0-alpha.6",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2768,7 +2768,7 @@
{
"name": "prettier-linter-helpers",
"version": "1.0.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2786,7 +2786,7 @@
{
"name": "process-nextick-args",
"version": "1.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2876,7 +2876,7 @@
{
"name": "readable-stream",
"version": "1.0.31",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -2972,7 +2972,7 @@
{
"name": "rolldown",
"version": "1.1.5",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3128,7 +3128,7 @@
{
"name": "source-map-support",
"version": "0.5.19",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3158,7 +3158,7 @@
{
"name": "std-env",
"version": "4.0.0-rc.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3176,7 +3176,7 @@
{
"name": "string_decoder",
"version": "0.10.24",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3224,7 +3224,7 @@
{
"name": "tailwindcss",
"version": "4.3.3",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3272,7 +3272,7 @@
{
"name": "tinybench",
"version": "2.9.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3290,13 +3290,13 @@
{
"name": "tinyqueue",
"version": "3.0.0",
- "author": "—",
+ "author": "-",
"license": "ISC"
},
{
"name": "tinyrainbow",
"version": "3.1.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3380,13 +3380,13 @@
{
"name": "undici",
"version": "7.28.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "undici-types",
"version": "7.16.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3404,7 +3404,7 @@
{
"name": "unzipit",
"version": "2.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3440,19 +3440,19 @@
{
"name": "uuid",
"version": "14.0.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
"name": "vis-data",
"version": "7.1.10",
- "author": "—",
+ "author": "-",
"license": "(Apache-2.0 OR MIT)"
},
{
"name": "vis-network",
"version": "9.1.13",
- "author": "—",
+ "author": "-",
"license": "(Apache-2.0 OR MIT)"
},
{
@@ -3494,7 +3494,7 @@
{
"name": "vue-component-type-helpers",
"version": "3.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3518,7 +3518,7 @@
{
"name": "vue-tsc",
"version": "3.3.7",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3530,7 +3530,7 @@
{
"name": "w3c-xmlserializer",
"version": "5.0.0",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3542,7 +3542,7 @@
{
"name": "web-worker",
"version": "1.5.0",
- "author": "—",
+ "author": "-",
"license": "Apache-2.0"
},
{
@@ -3662,7 +3662,7 @@
{
"name": "yargs",
"version": "12.0.5",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
@@ -3680,7 +3680,7 @@
{
"name": "zarrita",
"version": "0.7.1",
- "author": "—",
+ "author": "-",
"license": "MIT"
},
{
diff --git a/meshchatx/src/backend/data/plugins/mcx-bugs/frontend/main.js b/meshchatx/src/backend/data/plugins/mcx-bugs/frontend/main.js
index 59732ddf..c652f2cd 100644
--- a/meshchatx/src/backend/data/plugins/mcx-bugs/frontend/main.js
+++ b/meshchatx/src/backend/data/plugins/mcx-bugs/frontend/main.js
@@ -13,7 +13,7 @@ function formatLabel(api, key, params = {}) {
function shortHash(hash) {
if (!hash || hash.length < 12) {
- return hash || "—";
+ return hash || "-";
}
return `${hash.slice(0, 10)}…${hash.slice(-6)}`;
}
@@ -205,7 +205,7 @@ export async function activate(api) {
{
type: "text",
variant: "caption",
- value: entry.name || "—",
+ value: entry.name || "-",
},
{
type: "button",
@@ -319,7 +319,7 @@ export async function activate(api) {
{
type: "text",
variant: "subtitle",
- value: entry.title || "—",
+ value: entry.title || "-",
},
{
type: "text",
@@ -335,7 +335,7 @@ export async function activate(api) {
String(entry.description || "").slice(
0,
160
- ) || "—",
+ ) || "-",
},
],
},
diff --git a/meshchatx/src/backend/data/plugins/mcx-bugs/locales/en.json b/meshchatx/src/backend/data/plugins/mcx-bugs/locales/en.json
index ddc87845..aba86466 100644
--- a/meshchatx/src/backend/data/plugins/mcx-bugs/locales/en.json
+++ b/meshchatx/src/backend/data/plugins/mcx-bugs/locales/en.json
@@ -33,7 +33,7 @@
"no_preview": "Preview logs to review what will be sent.",
"preview_stats": "{lines} lines, {chars} characters",
"send_ok": "Report sent ({bytes} bytes).",
- "invalid_hash": "\"{value}\" is not a valid collector hash. Click 'Use my collector' to auto-fill, or paste a 32–64 character hex hash.",
+ "invalid_hash": "\"{value}\" is not a valid collector hash. Click 'Use my collector' to auto-fill, or paste a 32-64 character hex hash.",
"collector_started": "Collector started.",
"collector_stopped": "Collector stopped.",
"announce_ok": "Collector announced as mcx-bugs-v1.",
diff --git a/meshchatx/src/backend/diagnostics/memory_diagnostics.py b/meshchatx/src/backend/diagnostics/memory_diagnostics.py
index b9ada4b5..8f027f41 100644
--- a/meshchatx/src/backend/diagnostics/memory_diagnostics.py
+++ b/meshchatx/src/backend/diagnostics/memory_diagnostics.py
@@ -396,7 +396,7 @@ class MemoryDiagnostics:
type_name: str,
max_results: int = 20,
) -> list[dict[str, Any]]:
- """Find referrers of objects of a given type — useful to trace who holds references."""
+ """Find referrers of objects of a given type. Useful to trace who holds references."""
matches: list[Any] = []
try:
for obj in gc.get_objects():
diff --git a/meshchatx/src/backend/licenses_collector.py b/meshchatx/src/backend/licenses_collector.py
index 3fb4ea7d..07265c1c 100644
--- a/meshchatx/src/backend/licenses_collector.py
+++ b/meshchatx/src/backend/licenses_collector.py
@@ -42,7 +42,7 @@ def _license_from_metadata(meta: importlib.metadata.Metadata) -> str:
for line in classifiers:
if line.startswith("License ::"):
return line.split("::", 1)[-1].strip()
- return "—"
+ return "-"
def _author_from_metadata(meta: importlib.metadata.Metadata) -> str:
@@ -55,7 +55,7 @@ def _author_from_metadata(meta: importlib.metadata.Metadata) -> str:
m = (meta.get("Maintainer") or "").strip()
if m:
return m
- return "—"
+ return "-"
def _dist_for_requirement_name(name: str) -> importlib.metadata.Distribution | None:
@@ -161,8 +161,8 @@ def _bundled_vendor_license_row(
if name != dist_name and name != lookup_name:
return None
version = proj.get("version")
- version_s = version.strip() if isinstance(version, str) and version.strip() else "—"
- author = "—"
+ version_s = version.strip() if isinstance(version, str) and version.strip() else "-"
+ author = "-"
authors = proj.get("authors")
if isinstance(authors, list) and authors:
first = authors[0]
@@ -175,11 +175,11 @@ def _bundled_vendor_license_row(
author = an or ae
lic = proj.get("license")
if isinstance(lic, dict):
- license_s = str(lic.get("text") or lic.get("file") or "—").strip() or "—"
+ license_s = str(lic.get("text") or lic.get("file") or "-").strip() or "-"
elif isinstance(lic, str) and lic.strip():
license_s = lic.strip()
else:
- license_s = "—"
+ license_s = "-"
return {
"name": dist_name,
"version": version_s,
@@ -281,7 +281,7 @@ def _license_from_package_json(data: dict[str, Any]) -> str:
t = first.get("type")
if isinstance(t, str) and t.strip():
return t.strip()
- return "—"
+ return "-"
def _author_from_package_json(data: dict[str, Any]) -> str:
@@ -307,7 +307,7 @@ def _author_from_package_json(data: dict[str, Any]) -> str:
return f"{name} <{email}>"
if name or email:
return name or email
- return "—"
+ return "-"
def _workspace_root_npm_identity(repo_root: Path) -> tuple[str | None, str | None]:
@@ -436,10 +436,10 @@ def _flatten_pnpm_licenses_json(data: dict[str, Any]) -> list[dict[str, Any]]:
name = pkg.get("name") or "?"
versions = pkg.get("versions") or []
version = versions[0] if versions else "?"
- author = pkg.get("author") or "—"
+ author = pkg.get("author") or "-"
if not isinstance(author, str):
author = str(author)
- lic = pkg.get("license") or _license_key or "—"
+ lic = pkg.get("license") or _license_key or "-"
out.append(
{
"name": name,
@@ -552,8 +552,8 @@ def render_third_party_notices(payload: dict[str, Any]) -> str:
continue
name = str(row.get("name", "?"))
version = str(row.get("version", "?"))
- author = str(row.get("author", "—"))
- license_name = str(row.get("license", "—"))
+ author = str(row.get("author", "-"))
+ license_name = str(row.get("license", "-"))
lines.append(f"{name} {version}")
lines.append(f" License: {license_name}")
lines.append(f" Author: {author}")
diff --git a/meshchatx/src/frontend/components/App.vue b/meshchatx/src/frontend/components/App.vue
index 1d3073e0..7fbf59f2 100644
--- a/meshchatx/src/frontend/components/App.vue
+++ b/meshchatx/src/frontend/components/App.vue
@@ -1657,7 +1657,7 @@ export default {
userFacing,
};
- // Open peers are mark-as-read by ConversationViewer; still refresh for other peers.
+ // Open peers are mark-as-read by ConversationViewer. Still refresh for other peers.
if (isIncoming && userFacing && !sourceOpen) {
this.updateUnreadConversationsCount();
}
@@ -2121,7 +2121,7 @@ export default {
this.ringtonePlayer.play().catch((e) => {
if (e?.name === "NotAllowedError") {
// Browser autoplay policy blocked playback until user gesture.
- // Stop retry spam; we retry once user interacts again.
+ // Stop retry spam. We retry once user interacts again.
this.ringtoneAutoplayBlocked = true;
return;
}
@@ -2357,7 +2357,7 @@ export default {
return;
}
} catch {
- /* not a valid URL; continue */
+ /* not a valid URL, continue */
}
if (/^(meshchatx|meshchat):\/\/map\b/i.test(normalizedUrl)) {
WebSocketConnection.send(
diff --git a/meshchatx/src/frontend/components/about/AboutPage.vue b/meshchatx/src/frontend/components/about/AboutPage.vue
index 5d79c661..c27c07a7 100644
--- a/meshchatx/src/frontend/components/about/AboutPage.vue
+++ b/meshchatx/src/frontend/components/about/AboutPage.vue
@@ -1878,7 +1878,7 @@ export default {
},
formatRecoveryResult(value) {
if (value === null || value === undefined) {
- return "—";
+ return "-";
}
if (Array.isArray(value)) {
return value.join(", ");
diff --git a/meshchatx/src/frontend/components/debug/DebugLogsPage.vue b/meshchatx/src/frontend/components/debug/DebugLogsPage.vue
index 9b678188..de591dcd 100644
--- a/meshchatx/src/frontend/components/debug/DebugLogsPage.vue
+++ b/meshchatx/src/frontend/components/debug/DebugLogsPage.vue
@@ -198,7 +198,7 @@
<span class="text-gray-500">IP</span> {{ row.client_ip }}
</div>
<div class="text-gray-600 dark:text-gray-400 break-all max-sm:text-[8px]">
- <span class="text-gray-500">UA</span> {{ row.user_agent || "—" }}
+ <span class="text-gray-500">UA</span> {{ row.user_agent || "-" }}
</div>
<div v-if="row.detail" class="text-gray-500 max-sm:text-[8px] sm:text-[10px]">
{{ row.detail }}
diff --git a/meshchatx/src/frontend/components/interfaces/AddInterfacePage.vue b/meshchatx/src/frontend/components/interfaces/AddInterfacePage.vue
index b387d70a..ddf61380 100644
--- a/meshchatx/src/frontend/components/interfaces/AddInterfacePage.vue
+++ b/meshchatx/src/frontend/components/interfaces/AddInterfacePage.vue
@@ -528,7 +528,7 @@
<input
v-model="newInterfaceNetworkDevice"
type="text"
- placeholder="e.g. eth0 (real OS interface; leave empty to use Listen IP only)"
+ placeholder="e.g. eth0 (real OS interface, leave empty to use Listen IP only)"
class="input-field"
/>
</div>
@@ -1333,7 +1333,7 @@
</div>
</div>
- <!-- LocalInterface: IPC path used internally by RNS; optional external module -->
+ <!-- LocalInterface: IPC path used internally by RNS. Optional external module -->
<div v-if="newInterfaceType === 'LocalInterface'" class="space-y-4">
<div class="text-sm text-gray-800 dark:text-zinc-200 leading-snug">
{{ $t("interfaces.loopback_local_title") }}
diff --git a/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue b/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue
index f0583fa1..2c3d5f7d 100644
--- a/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue
+++ b/meshchatx/src/frontend/components/interfaces/InterfacesPage.vue
@@ -217,8 +217,8 @@
>
</div>
<div class="text-sm text-gray-600 dark:text-gray-300">
- Discovery runs continually; heard announces stay listed. Connected entries show
- a green pill; disconnected entries are dimmed with a red label.
+ Discovery runs continually. Heard announces stay listed. Connected entries show
+ a green pill. Disconnected entries are dimmed with a red label.
</div>
</div>
<div class="flex flex-wrap gap-2 items-center shrink-0 min-w-0">
@@ -900,7 +900,7 @@ export default {
},
discoveredEmptyMessage() {
if (!this.isReticulumRunning) {
- return "LXMF/Reticulum is not running; discovery cannot listen for announces.";
+ return "LXMF/Reticulum is not running. Discovery cannot listen for announces.";
}
if (!this.discoveryConfig.discover_interfaces) {
return "Discovery is disabled. Enable it to start listening for announces.";
diff --git a/meshchatx/src/frontend/components/map/MapPage.vue b/meshchatx/src/frontend/components/map/MapPage.vue
index 00e4daeb..f955b0f6 100644
--- a/meshchatx/src/frontend/components/map/MapPage.vue
+++ b/meshchatx/src/frontend/components/map/MapPage.vue
@@ -437,7 +437,7 @@
</div>
</div>
- <!-- scale line: bottom-right so it never stacks over the lat/lon readout (bottom-left); hidden on small screens -->
+ <!-- scale line: bottom-right so it never stacks over the lat/lon readout (bottom-left). Hidden on small screens -->
<div
v-show="!isMobileScreen"
ref="scaleLineMount"
@@ -4673,7 +4673,7 @@ export default {
this.onVectorExchangeImport({ features, merge: true });
} catch (e) {
console.error("Map drop import failed:", e);
- ToastUtils.error(this.$t("map.vector_import_failed") + ` — ${file.name}`);
+ ToastUtils.error(this.$t("map.vector_import_failed") + ` - ${file.name}`);
}
}
},
diff --git a/meshchatx/src/frontend/components/messages/ConversationViewer.vue b/meshchatx/src/frontend/components/messages/ConversationViewer.vue
index 714b9c10..629558ef 100644
--- a/meshchatx/src/frontend/components/messages/ConversationViewer.vue
+++ b/meshchatx/src/frontend/components/messages/ConversationViewer.vue
@@ -2074,7 +2074,7 @@ export default {
const d = { ...this.rawMessageData };
const c = d.content;
if (typeof c === "string" && c.length > MESSAGE_BODY_MAX_DISPLAY_CHARS) {
- d.content = `[Omitted ${c.length} characters — use Copy full text above]`;
+ d.content = `[Omitted ${c.length} characters. Use Copy full text above]`;
}
try {
return JSON.stringify(d, null, 2);
@@ -3117,8 +3117,8 @@ export default {
showFooter: Boolean(hasTr),
showOriginalLink: hasTr && !tr.showOriginal,
showTranslationLink: hasTr && tr.showOriginal,
- fromCode: (tr && tr.resolvedSourceLang) || "–",
- toCode: (tr && tr.targetLang) || "–",
+ fromCode: (tr && tr.resolvedSourceLang) || "-",
+ toCode: (tr && tr.targetLang) || "-",
messageHash: h,
singleEmoji,
};
diff --git a/meshchatx/src/frontend/components/page-nodes/PageNodesPage.vue b/meshchatx/src/frontend/components/page-nodes/PageNodesPage.vue
index 5411d491..35b397e5 100644
--- a/meshchatx/src/frontend/components/page-nodes/PageNodesPage.vue
+++ b/meshchatx/src/frontend/components/page-nodes/PageNodesPage.vue
@@ -580,7 +580,7 @@ export default {
return (bytes / (1024 * 1024)).toFixed(1) + " MB";
},
formatMeshUptime(seconds) {
- if (seconds == null || seconds < 0) return "—";
+ if (seconds == null || seconds < 0) return "-";
let s = Math.floor(seconds);
if (s < 60) return `${s}s`;
if (s < 3600) return `${Math.floor(s / 60)}m`;
diff --git a/meshchatx/src/frontend/components/relay/RelayChatPage.vue b/meshchatx/src/frontend/components/relay/RelayChatPage.vue
index b3f97615..5dc6f227 100644
--- a/meshchatx/src/frontend/components/relay/RelayChatPage.vue
+++ b/meshchatx/src/frontend/components/relay/RelayChatPage.vue
@@ -1790,7 +1790,7 @@ export default {
},
formatUptime(seconds) {
if (seconds == null || seconds < 0) {
- return "—";
+ return "-";
}
let s = Math.floor(seconds);
if (s < 60) {
@@ -2545,7 +2545,7 @@ export default {
try {
await window.api.post(`/api/v1/rrc/hubs/${hubHash}/rooms/${this.encodeRoom(room)}/read`);
} catch {
- // GET messages already marks active/read on the server; ignore duplicate failures.
+ // GET messages already marks active/read on the server. Ignore duplicate failures.
}
if (refreshHubs) {
await this.fetchHubs();
diff --git a/meshchatx/src/frontend/components/relay/RelayHostModerationPage.vue b/meshchatx/src/frontend/components/relay/RelayHostModerationPage.vue
index f42a0bc6..bd32e7a2 100644
--- a/meshchatx/src/frontend/components/relay/RelayHostModerationPage.vue
+++ b/meshchatx/src/frontend/components/relay/RelayHostModerationPage.vue
@@ -881,7 +881,7 @@ export default {
},
formatUptime(seconds) {
if (seconds == null || seconds < 0) {
- return "—";
+ return "-";
}
let s = Math.floor(seconds);
if (s < 60) {
diff --git a/meshchatx/src/frontend/components/rncp/RNCPPage.vue b/meshchatx/src/frontend/components/rncp/RNCPPage.vue
index 73353e32..f9782d72 100644
--- a/meshchatx/src/frontend/components/rncp/RNCPPage.vue
+++ b/meshchatx/src/frontend/components/rncp/RNCPPage.vue
@@ -627,7 +627,7 @@ export default {
const f = event.target.files?.[0];
event.target.value = "";
if (!f) return;
- // Browsers only expose the basename; require an explicit full path.
+ // Browsers only expose the basename. Require an explicit full path.
this.sendFilePath = "";
DialogUtils.alert(this.$t("rncp.web_path_hint"));
},
diff --git a/meshchatx/src/frontend/components/rnode/RNodeDiagnosticsPanel.vue b/meshchatx/src/frontend/components/rnode/RNodeDiagnosticsPanel.vue
index adef15bc..9efba195 100644
--- a/meshchatx/src/frontend/components/rnode/RNodeDiagnosticsPanel.vue
+++ b/meshchatx/src/frontend/components/rnode/RNodeDiagnosticsPanel.vue
@@ -30,7 +30,7 @@
{{ $t(row.labelKey) }}
</dt>
<dd class="font-mono text-gray-800 dark:text-zinc-200 break-all">
- {{ row.value || "—" }}
+ {{ row.value || "-" }}
</dd>
</div>
</dl>
diff --git a/meshchatx/src/frontend/components/settings/SettingsPage.vue b/meshchatx/src/frontend/components/settings/SettingsPage.vue
index 9b9da5ca..78b64901 100644
--- a/meshchatx/src/frontend/components/settings/SettingsPage.vue
+++ b/meshchatx/src/frontend/components/settings/SettingsPage.vue
@@ -3133,8 +3133,8 @@
{{ $t("app.web_listen_address") }}
</div>
<div class="font-mono text-gray-900 dark:text-gray-100">
- {{ serverSecurity.listen_host || "—" }}:{{
- serverSecurity.listen_port ?? "—"
+ {{ serverSecurity.listen_host || "-" }}:{{
+ serverSecurity.listen_port ?? "-"
}}
</div>
</div>
@@ -3564,7 +3564,7 @@
$t("app.run_local_node_description")
}}</span>
<span class="setting-toggle__hint monospace-field">{{
- config.lxmf_local_propagation_node_address_hash || "—"
+ config.lxmf_local_propagation_node_address_hash || "-"
}}</span>
</span>
</label>
diff --git a/meshchatx/src/frontend/components/tools/BotsPage.vue b/meshchatx/src/frontend/components/tools/BotsPage.vue
index 5ba41976..dd0c1fd9 100644
--- a/meshchatx/src/frontend/components/tools/BotsPage.vue
+++ b/meshchatx/src/frontend/components/tools/BotsPage.vue
@@ -244,7 +244,7 @@
<span v-else-if="lxmfAddressFor(bot)">{{
$t("bots.never_announced")
}}</span>
- <span v-else>—</span>
+ <span v-else>-</span>
</dd>
</div>
</dl>
diff --git a/meshchatx/src/frontend/components/tools/RNodeFlasherPage.vue b/meshchatx/src/frontend/components/tools/RNodeFlasherPage.vue
index a8e5f73f..db325457 100644
--- a/meshchatx/src/frontend/components/tools/RNodeFlasherPage.vue
+++ b/meshchatx/src/frontend/components/tools/RNodeFlasherPage.vue
@@ -339,7 +339,7 @@ export default {
try {
await this._loadScript(lib);
} catch {
- // continue best-effort; loadVendorLibraries is best-effort and
+ // continue best-effort. loadVendorLibraries is best-effort and
// missing assets surface later through clear error toasts
}
}
diff --git a/meshchatx/src/frontend/js/LinkUtils.js b/meshchatx/src/frontend/js/LinkUtils.js
index d566508b..7bafcb6f 100644
--- a/meshchatx/src/frontend/js/LinkUtils.js
+++ b/meshchatx/src/frontend/js/LinkUtils.js
@@ -79,7 +79,7 @@ export default class LinkUtils {
/**
* Detects and wraps Reticulum (NomadNet and LXMF) links in HTML.
* Supports nomadnet://<hash>, nomadnet@<hash>, lxmf://<hash>, lxmf@<hash>, lxmf:<hash>,
- * and bare <hash>:/path (NomadNet path form only — no bare hash without prefix).
+ * and bare <hash>:/path (NomadNet path form only, no bare hash without prefix).
*/
static renderReticulumLinks(text) {
if (!text) return "";
diff --git a/meshchatx/src/frontend/js/MarkdownRenderer.js b/meshchatx/src/frontend/js/MarkdownRenderer.js
index 943a753e..2805ab68 100644
--- a/meshchatx/src/frontend/js/MarkdownRenderer.js
+++ b/meshchatx/src/frontend/js/MarkdownRenderer.js
@@ -61,7 +61,7 @@ export default class MarkdownRenderer {
// Links
text = LinkUtils.renderAllLinks(text);
- // Restore inline code then fenced blocks (single pass; avoid O(n*m) replace loops)
+ // Restore inline code then fenced blocks (single pass, avoid O(n*m) replace loops)
text = text.replace(/\[\[IC(\d+)\]\]/g, (_m, idx) => inline_codes[Number(idx)] ?? _m);
text = text.replace(/\[\[CB(\d+)\]\]/g, (_m, idx) => code_blocks[Number(idx)] ?? _m);
diff --git a/meshchatx/src/frontend/js/NomadPageRenderer.js b/meshchatx/src/frontend/js/NomadPageRenderer.js
index 70105ac9..3469b258 100644
--- a/meshchatx/src/frontend/js/NomadPageRenderer.js
+++ b/meshchatx/src/frontend/js/NomadPageRenderer.js
@@ -49,7 +49,7 @@ function normalizeMarkdownInput(md) {
}
let s = String(md);
s = s.replace(/\r\n/g, "\n");
- // CommonMark requires a space after # for ATX headings; "#Title" is not a heading
+ // CommonMark requires a space after # for ATX headings. "#Title" is not a heading
s = s.replace(/^(\s{0,3})(#{1,6})([^\s#])/gm, "$1$2 $3");
return s;
}
diff --git a/meshchatx/src/frontend/js/browserLayoutStore.js b/meshchatx/src/frontend/js/browserLayoutStore.js
index 0996b092..25d40cea 100644
--- a/meshchatx/src/frontend/js/browserLayoutStore.js
+++ b/meshchatx/src/frontend/js/browserLayoutStore.js
@@ -45,7 +45,7 @@ function writeJson(key, value) {
}
window.localStorage.setItem(key, JSON.stringify(value));
} catch {
- // persistence is best-effort; ignore quota or availability errors
+ // persistence is best-effort. Ignore quota or availability errors
}
}
diff --git a/meshchatx/src/frontend/js/clipboardUtils.js b/meshchatx/src/frontend/js/clipboardUtils.js
index bcd38703..752519dc 100644
--- a/meshchatx/src/frontend/js/clipboardUtils.js
+++ b/meshchatx/src/frontend/js/clipboardUtils.js
@@ -4,7 +4,7 @@
*/
/**
- * Browsers set `false` on http://0.0.0.0 and similar; `undefined` in some test envs is treated as allowed.
+ * Browsers set `false` on http://0.0.0.0 and similar. `undefined` in some test envs is treated as allowed.
* @returns {boolean}
*/
export function isWindowSecureContext() {
diff --git a/meshchatx/src/frontend/js/networkVisualiserWebGLEngine.js b/meshchatx/src/frontend/js/networkVisualiserWebGLEngine.js
index ee5c5a54..f67a0e18 100644
--- a/meshchatx/src/frontend/js/networkVisualiserWebGLEngine.js
+++ b/meshchatx/src/frontend/js/networkVisualiserWebGLEngine.js
@@ -148,7 +148,7 @@ function colorFromNode(node) {
if (rgb) return rgb;
}
if (c && typeof c === "object") {
- // Border is the vivid badge color; background is a pale tint for vis-network.
+ // Border is the vivid badge color. Background is a pale tint for vis-network.
const fill = c.border || c.background;
const rgb = hexToRgb01(fill);
if (rgb) return rgb;
diff --git a/meshchatx/src/frontend/locales/de.json b/meshchatx/src/frontend/locales/de.json
index 0d4d9543..f48f8d34 100644
--- a/meshchatx/src/frontend/locales/de.json
+++ b/meshchatx/src/frontend/locales/de.json
@@ -195,7 +195,7 @@
"run_local_node": "Einen lokalen Propagationsknoten betreiben",
"run_local_node_description": "MeshChat wird einen Knoten unter Verwendung dieses lokalen Ziel-Hashs ankündigen und warten.",
"preferred_propagation_node": "Bevorzugter Propagationsknoten",
- "preferred_node_placeholder": "Ziel-Hash, z.B. a39610c89d18bb48c73e429582423c24",
+ "preferred_node_placeholder": "Ziel-Hash, z.B. A39610c89d18bb48c73e429582423c24",
"fallback_node_description": "Nachrichten weichen auf diesen Knoten aus, wenn die direkte Zustellung fehlschlägt.",
"auto_select_node": "Besten Knoten automatisch auswählen",
"auto_select_node_description": "Automatisch den besten verfügbaren Propagationsknoten basierend auf den Netzwerkhops finden und dorthin wechseln.",
@@ -356,7 +356,7 @@
"announce_store_lxst": "LXST Telefonie (lxst.telephony)",
"announce_store_nomad": "NomadNet-Knoten (nomadnetwork.node)",
"announce_store_prop": "Verteilerknoten (lxmf.propagation)",
- "announce_max_stored_heading": "Max. gespeichert pro Aspekt (Datenbank)",
+ "announce_max_stored_heading": "Max. Gespeichert pro Aspekt (Datenbank)",
"announce_fetch_limit_heading": "Standard-Seitengröße (API)",
"announce_search_max_fetch": "Max. Ankündigungen für Suche",
"announce_search_max_fetch_hint": "Maximale Zeilen aus der Datenbank bei Textsuche (pro Anfrage).",
@@ -395,7 +395,7 @@
"csp_settings": "Content-Security-Policy (CSP)",
"csp_description": "Fügen Sie manuell erlaubte Domänen für verschiedene Ressourcen hinzu. Dies ist nützlich für eigene Kartenserver, Bots oder externe Integrationen.",
"csp_extra_connect_src": "Zusätzliche Connect-Quellen",
- "csp_extra_connect_src_description": "Zusätzliche Quellen für fetch/Websocket-Verbindungen (z. B. https://api.example.com).",
+ "csp_extra_connect_src_description": "Zusätzliche Quellen für fetch/Websocket-Verbindungen (z. B. Https://api.example.com).",
"csp_extra_img_src": "Zusätzliche Bildquellen",
"csp_extra_img_src_description": "Zusätzliche Quellen für Bilder und Kacheln.",
"csp_extra_frame_src": "Zusätzliche Frame-Quellen",
@@ -416,7 +416,7 @@
"location_manual_lon": "Längengrad",
"location_manual_alt": "Höhe (m)",
"telemetry_enabled": "Telemetrie aktiviert",
- "telemetry_description": "Erlaubt MeshChatX das Senden und Empfangen von Telemetriedaten, wie z. B. das Beantworten von Standortanfragen vertrauenswürdiger Kontakte.",
+ "telemetry_description": "Erlaubt MeshChatX das Senden und Empfangen von Telemetriedaten, wie z. B. Das Beantworten von Standortanfragen vertrauenswürdiger Kontakte.",
"telemetry_trusted_peers": "Vertrauenswürdige Telemetrie-Peers",
"telemetry_no_trusted_peers": "Keine vertrauenswürdigen Peers konfiguriert. Vertrauen Sie Peers aus dem Chat-Menü, um ihnen zu erlauben, Ihren Standort abzufragen.",
"telemetry_revoke_trust": "Vertrauen entziehen",
@@ -435,9 +435,9 @@
"map_overlay_limits_desc": "Schutzgrenzen für NomadNet- und RNGit-Importe von KMZ/KML/GeoJSON. Werte werden auf sichere Bereiche begrenzt.",
"map_overlay_max_bytes": "Max. Overlay-Dateigröße (Bytes)",
"map_overlay_max_features": "Max. Features pro Overlay",
- "map_overlay_max_kmz_uncompressed_bytes": "Max. unkomprimierte KMZ-Größe (Bytes)",
+ "map_overlay_max_kmz_uncompressed_bytes": "Max. Unkomprimierte KMZ-Größe (Bytes)",
"map_overlay_max_sources": "Max. Overlay-Quellen",
- "map_overlay_max_concurrent_jobs": "Max. gleichzeitige Overlay-Jobs",
+ "map_overlay_max_concurrent_jobs": "Max. Gleichzeitige Overlay-Jobs",
"map_overlay_path_timeout_seconds": "Timeout für Pfadsuche (Sekunden)",
"map_overlay_transfer_timeout_seconds": "Timeout für Übertragung (Sekunden)",
"map_overlay_job_timeout_seconds": "Timeout für Job (Sekunden)",
@@ -486,7 +486,7 @@
"copy_rpc_config": "RPC-Konfiguration kopieren",
"connected_to_shared_instance": "Dieser MeshChatX-Prozess ist mit einer externen gemeinsam genutzten Reticulum-Instanz verbunden. Bearbeiten Sie die Konfiguration dieser Instanz, um die Share/RPC-Einstellungen zu ändern.",
"copy_failed": "In Zwischenablage kopieren fehlgeschlagen",
- "rpc_key_hidden": "Verborgen — tippen zum Anzeigen",
+ "rpc_key_hidden": "Verborgen. Tippen zum Anzeigen",
"rpc_key_show": "RPC-Schlüssel anzeigen",
"rpc_key_hide": "RPC-Schlüssel verbergen",
"refresh_community_interfaces": "Von directory.rns.recipes aktualisieren",
@@ -599,7 +599,7 @@
"file_too_large_video": "WEBM-Video-Sticker dürfen höchstens 256 KB groß sein.",
"invalid_dimensions": "Sticker dürfen höchstens 512×512 Pixel haben, eine Seite muss genau 512 sein.",
"invalid_duration": "Animierte und Video-Sticker dürfen höchstens 3 Sekunden lang sein.",
- "invalid_fps": "Animierte Sticker: 30–60 FPS, Video-Sticker bis 30 FPS.",
+ "invalid_fps": "Animierte Sticker: 30-60 FPS, Video-Sticker bis 30 FPS.",
"invalid_codec": "Video-Sticker müssen den Codec VP9 verwenden.",
"invalid_audio": "Video-Sticker dürfen keinen Audiostream enthalten.",
"invalid_tgs": "TGS-Animation konnte nicht gelesen werden.",
@@ -613,7 +613,7 @@
"title_label": "Titel",
"title_placeholder": "Mein Paket",
"short_name_label": "Kurzname",
- "short_name_placeholder": "z. B. katzen",
+ "short_name_placeholder": "z. B. Katzen",
"description_label": "Beschreibung",
"type_label": "Typ",
"type_static": "Statisch",
@@ -1047,7 +1047,7 @@
"contact_name_optional": "Kontaktname (optional)",
"contact_name_placeholder": "z.B. Feldknoten",
"hash_or_uri": "LXMF-Hash oder URI",
- "hash_or_uri_placeholder": "32-Zeichen-Hash, lxmf://... oder lxma://...",
+ "hash_or_uri_placeholder": "32-Zeichen-Hash, lxmf://... Oder lxma://...",
"paste": "Einfügen",
"failed_load_contacts": "Kontakte konnten nicht geladen werden",
"invalid_contact_input": "Ungültige Eingabe. 32-Zeichen-Hash oder lxma://-URI verwenden.",
@@ -1467,8 +1467,8 @@
"tile_connectivity_retry": "Kacheln erneut laden",
"tile_connectivity_use_offline": "Offline-Modus",
"tile_connectivity_dismiss": "Schließen",
- "tile_failover_trying": "Kartenkacheln fehlgeschlagen; versuche {provider}…",
- "tile_failover_offline": "Online-Kacheln nicht verfügbar; wechsle in den Offline-Modus",
+ "tile_failover_trying": "Kartenkacheln fehlgeschlagen. Versuche {provider}…",
+ "tile_failover_offline": "Online-Kacheln nicht verfügbar. Wechsle in den Offline-Modus",
"search_no_results": "Keine Ergebnisse gefunden",
"custom_tile_server_unavailable": "Benutzerdefinierter Kachelserver ist im Offline-Modus nicht erreichbar",
"custom_nominatim_unavailable": "Benutzerdefinierter Nominatim-Server ist im Offline-Modus nicht erreichbar",
@@ -1645,7 +1645,7 @@
"snr": "SNR {snr}",
"stamp_cost": "Stempelkosten {cost}",
"stamp_ticket_valid": "Ausgehendes Stempel-Ticket aktiv (sofortiger Versand). Läuft ab {expires}.",
- "stamp_ticket_expired": "Stempel-Ticket abgelaufen; die nächste Nachricht kann einen Proof-of-Work erfordern.",
+ "stamp_ticket_expired": "Stempel-Ticket abgelaufen. Die nächste Nachricht kann einen Proof-of-Work erfordern.",
"pop_out_chat": "Chat auslagern",
"open_in_split": "In geteilter Ansicht öffnen",
"select_conversation_for_pane": "Wähle eine Unterhaltung, um sie hier zu öffnen",
@@ -1675,7 +1675,7 @@
"path_loading": "Pfad wird geprüft…",
"path_stale_label": "veraltet",
"path_unresponsive_label": "ohne Antwort",
- "path_stale_hint": "Dieser Pfadeintrag ist abgelaufen und wird vor dem nächsten Senden ggf. neu ermittelt.",
+ "path_stale_hint": "Dieser Pfadeintrag ist abgelaufen und wird vor dem nächsten Senden ggf. Neu ermittelt.",
"path_unresponsive_hint": "Reticulum hat diesen Pfad als nicht ansprechbar markiert.",
"path_info_title": "Pfadinformation",
"path_already_available": "Pfad ist bereits verfügbar",
@@ -1760,7 +1760,7 @@
"folders_imported": "Ordner importiert",
"failed_import_folders": "Ordner konnten nicht importiert werden",
"failed_read_clipboard": "Lesen aus Zwischenablage fehlgeschlagen",
- "clipboard_read_requires_secure_context": "Zum Lesen der Zwischenablage wird eine sichere Seite benötigt (https oder http://127.0.0.1). Die Verwendung von http://0.0.0.0 oder einem anderen nicht-lokalen HTTP-Ursprung blockiert die Clipboard-API – öffnen Sie die App auf 127.0.0.1 oder https, oder fügen Sie mit Strg+V ein.",
+ "clipboard_read_requires_secure_context": "Zum Lesen der Zwischenablage wird eine sichere Seite benötigt (https oder http://127.0.0.1). Die Verwendung von http://0.0.0.0 oder einem anderen nicht-lokalen HTTP-Ursprung blockiert die Clipboard-API. Öffnen Sie die App auf 127.0.0.1 oder https, oder fügen Sie mit Strg+V ein.",
"clipboard_write_unavailable": "Automatisches Kopieren nicht möglich. Öffnen Sie die Seite über https oder http://127.0.0.1, oder markieren Sie den Text und kopieren Sie ihn manuell.",
"oversized_body_notice": "Diese Nachricht ist zu groß, um sie hier anzuzeigen ({count} Zeichen). Kopieren Sie den vollständigen Text, um ihn woanders zu lesen oder zu bearbeiten.",
"oversized_body_copy": "Volltext kopieren",
@@ -1819,7 +1819,7 @@
"failed_start_recording": "Aufnahme starten fehlgeschlagen",
"failed_start_recording_help_mediadevices": "Diese Webansicht stellt keine Mikrofon-API bereit.",
"failed_start_recording_help_web_audio": "Web Audio ist in diesem Browser nicht verfügbar.",
- "failed_start_recording_help_audio_worklet": "AudioWorklet wird nicht unterstützt — OPUS-Sprachnotizen benötigen es.",
+ "failed_start_recording_help_audio_worklet": "AudioWorklet wird nicht unterstützt. OPUS-Sprachnotizen benötigen es.",
"failed_start_recording_help_permission": "Mikrofonzugriff erlauben oder erneut versuchen, sobald eine andere App das Mikrofon freigibt.",
"remove_audio_confirm": "Sind Sie sicher, dass Sie diesen Audio-Anhang entfernen möchten?",
"failed_generate_qr": "QR-Code konnte nicht generiert werden",
@@ -2140,7 +2140,7 @@
"licenses": {
"section_label": "Rechtliches",
"title": "Lizenzen Dritter",
- "description": "Versionen, Autoren und SPDX- oder deklarierte Lizenzangaben zur Laufzeit. Daten stammen aus der installierten Python-Umgebung. Frontend-Daten nutzen pnpm, falls es funktioniert. Sonst werden Pakete aus node_modules gelesen (z. B. wenn pnpm licenses an der Lockfile scheitert).",
+ "description": "Versionen, Autoren und SPDX- oder deklarierte Lizenzangaben zur Laufzeit. Daten stammen aus der installierten Python-Umgebung. Frontend-Daten nutzen pnpm, falls es funktioniert. Sonst werden Pakete aus node_modules gelesen (z. B. Wenn pnpm licenses an der Lockfile scheitert).",
"short_description": "Versionen, Autoren und Lizenzen für Backend und Frontend.",
"search_placeholder": "Pakete, Versionen, Autoren, Lizenzen suchen...",
"generated_at": "Erstellt um {time}",
@@ -2866,7 +2866,7 @@
"argos_translate": "Argos Translate",
"libretranslate": "LibreTranslate",
"api_server": "LibreTranslate API-Server",
- "api_server_description": "Geben Sie die Basis-URL Ihres LibreTranslate-Servers ein (z. B. http://localhost:5000)",
+ "api_server_description": "Geben Sie die Basis-URL Ihres LibreTranslate-Servers ein (z. B. Http://localhost:5000)",
"api_key_optional": "LibreTranslate API-Schlüssel (optional)",
"api_key_placeholder": "Leer lassen, sofern Ihr Anbieter keinen verlangt",
"api_key_description": "Falls erforderlich, erwartet LibreTranslate dies als api_key im JSON-Übersetzungskörper und optional bei der Sprachenanfrage. Bei typischen selbst gehosteten Instanzen weglassen.",
@@ -3344,14 +3344,14 @@
"micron_wasm_update_source_upload": "Lokale Datei (nicht verifiziert)",
"micron_wasm_update_active_size": "Größe: {bytes} Bytes",
"micron_wasm_update_bundled_only": "Es wird das mit der App gebündelte WASM verwendet. Es wird nichts aus dem Netz geladen, bis Sie eine der Aktionen unten nutzen.",
- "micron_wasm_update_isolation_note": "Der Browser kann WebAssembly nicht vollständig von JavaScript isolieren. Diese App stellt die Micron-Konvertierung nur über eine schmale API bereit; gerendertes HTML wird weiter bereinigt. wasm_exec.js stammt immer aus dem App-Bundle, passend zur Go-Laufzeit.",
+ "micron_wasm_update_isolation_note": "Der Browser kann WebAssembly nicht vollständig von JavaScript isolieren. Diese App stellt die Micron-Konvertierung nur über eine schmale API bereit. Gerendertes HTML wird weiter bereinigt. Wasm_exec.js stammt immer aus dem App-Bundle, passend zur Go-Laufzeit.",
"micron_wasm_update_github_heading": "Installation aus GitHub-Release",
"micron_wasm_update_github_url_hint": "Mit „Abrufen“ lädt der MeshChat-Server von GitHub herunter (Browser bleibt same-origin für CSP). Quellpfad:",
"micron_wasm_update_fetch_github": "Abrufen und installieren",
"micron_wasm_update_upload_heading": "Installation aus Datei",
"micron_wasm_update_upload_warning": "Hochgeladenes WASM wird nicht auf Echtheit geprüft. Nur Dateien verwenden, denen Sie vertrauen.",
"micron_wasm_update_revert_bundled": "Überschreibung entfernen (gebündeltes WASM)",
- "micron_wasm_update_err_empty_tag": "Release-Tag eingeben (z. B. v1.0.5).",
+ "micron_wasm_update_err_empty_tag": "Release-Tag eingeben (z. B. V1.0.5).",
"micron_wasm_update_err_not_wasm": "Wählen Sie eine Datei, deren Name auf .wasm endet.",
"micron_wasm_update_err_too_large": "Die Datei ist zu groß für eine Laufzeit-Überschreibung.",
"micron_wasm_update_err_too_small": "Die Datei ist zu klein für ein gültiges WASM-Modul.",
@@ -3398,7 +3398,7 @@
"reduce_background_polling": "Slow background status polling",
"reduce_background_polling_desc": "Multiplies App shell poll intervals (calls, unread, app info).",
"background_poll_multiplier": "Background poll multiplier",
- "background_poll_multiplier_desc": "2–10. Applied to App shell intervals when the option above is on.",
+ "background_poll_multiplier_desc": "2-10. Applied to App shell intervals when the option above is on.",
"reduce_interfaces_discovery": "Slow Interfaces page polls",
"reduce_interfaces_discovery_desc": "Uses the intervals below for stats and discovered-interface refresh.",
"interfaces_stats_poll_seconds": "Interfaces stats poll (seconds)",
diff --git a/meshchatx/src/frontend/locales/en.json b/meshchatx/src/frontend/locales/en.json
index de427826..810146d3 100644
--- a/meshchatx/src/frontend/locales/en.json
+++ b/meshchatx/src/frontend/locales/en.json
@@ -1,3962 +1,3962 @@
{
- "_languageName": "English",
- "bots": {
- "title": "LXMFy Bots",
- "description": "Run simple LXMF bots (echo, notes, reminders) when the LXMFy add-on is installed.",
- "bot_framework": "Bot Framework",
- "lxmfy_not_detected": "LXMFy not detected",
- "lxmfy_not_detected_desc": "To use bots, you must install the LXMFy package:",
- "install_via_pip": "Install via pip",
- "create_new_bot": "Create New Bot",
- "running_bots": "Running Bots",
- "no_bots_running": "No bots are currently running.",
- "select": "Select",
- "start_bot": "Start Bot",
- "stop_bot": "Stop Bot",
- "restart_bot": "Restart Bot",
- "saved_bots": "Saved Bots",
- "bot_name": "Bot Name",
- "cancel": "Cancel",
- "bot_started": "Bot started successfully",
- "bot_stopped": "Bot stopped",
- "start_pending": "Starting bot… LXMF and Reticulum can take a moment.",
- "stop_pending": "Stopping bot…",
- "restart_pending": "Restarting bot… this may take a moment.",
- "bot_ready": "Bot is up and running.",
- "failed_to_start": "Failed to start bot",
- "failed_to_stop": "Failed to stop bot",
- "delete_bot": "Delete Bot",
- "export_identity": "Export Identity",
- "bot_deleted": "Bot deleted successfully",
- "failed_to_delete": "Failed to delete bot",
- "more_bots_coming": "More bots coming soon!",
- "chat_with_bot": "Chat",
- "lxmf_address": "LXMF address",
- "last_announce": "Last announce",
- "never_announced": "Not seen on this node yet",
- "address_pending": "Not available until the bot has started once",
- "status_running": "Running",
- "status_stopped": "Stopped",
- "force_announce": "Announce now",
- "edit_name": "Edit name",
- "bot_renamed": "Bot name saved",
- "rename_failed": "Could not save name",
- "name_required": "Enter a name",
- "announce_triggered": "Announce requested",
- "announce_failed": "Could not request announce",
- "last_error_heading": "Last error",
- "view_process_log": "Process log (debug)",
- "process_log_title": "Bot process log",
- "process_log_empty": "No log output yet for this bot.",
- "process_log_truncated": "Only the end of the log is shown. The full file on disk is larger.",
- "process_log_loading": "Loading log…",
- "process_log_failed": "Could not load process log",
- "process_log_copied": "Log copied to clipboard",
- "copy_process_log": "Copy log"
- },
- "app": {
- "name": "Reticulum MeshChatX",
- "sync_messages": "Sync Messages",
- "compose": "Compose",
- "messages": "Messages",
- "contacts": "Contacts",
- "relay_chat": "Relay Chat",
- "nomad_network": "Nomad Network",
- "map": "Map",
- "archives": "Archives",
- "propagation_nodes": "Propagation Nodes",
- "network_visualiser": "Network Visualiser",
- "interfaces": "Interfaces",
- "tools": "Tools",
- "settings": "Settings",
- "identities": "Identities",
- "documentation": "Documentation",
- "about": "About",
- "changelog_title": "What's New",
- "do_not_show_again": "Do not show again for this version",
- "do_not_show_ever": "Do not show ever again",
- "tutorial_title": "Getting Started",
- "tutorial_welcome": "Welcome to MeshChatX",
- "tutorial_connect": "Connect to the Mesh",
- "tutorial_finish": "Ready to Roll!",
- "tutorial_restart_required": "Restart Required",
- "tutorial_docker_note": "If you're running in Docker, ensure you restart the container.",
- "my_identity": "My Identity",
- "identity_hash": "Identity Hash",
- "lxmf_address": "LXMF Address",
- "announce": "Announce",
- "announce_now": "Announce Now",
- "show_qr": "Show QR Code",
- "failed_announce": "failed to announce",
- "announce_sent": "Announcement sent",
- "notifications": "Notifications",
- "notification_sound_settings": "Message Notification Sound",
- "notification_sound_settings_description": "Play a custom sound when you receive a new message while MeshChat is open. Upload a sound file first, then enable playback.",
- "enable_notification_sound": "Enable notification sound",
- "enable_notification_sound_description": "Plays your chosen sound for incoming messages on desktop (Windows, macOS, and Linux).",
- "notification_sound_volume": "Notification volume",
- "notification_sound_default": "Default notification sound",
- "notification_sound_primary_default": "Primary uploaded sound",
- "notification_sounds": "Uploaded sounds",
- "notification_sound_upload": "Upload sound",
- "notification_sound_uploading": "Uploading...",
- "notification_sound_none_uploaded": "No notification sound uploaded yet. Upload an audio file to get started.",
- "notification_sound_primary": "Primary",
- "notification_sound_preview": "Preview sound",
- "notification_sound_set_primary": "Set primary",
- "notification_sound_remove": "Remove sound",
- "notification_sound_uploaded": "Notification sound uploaded",
- "notification_sound_upload_failed": "Failed to upload notification sound",
- "notification_sound_deleted": "Notification sound deleted",
- "notification_sound_delete_failed": "Failed to delete notification sound",
- "notification_sound_primary_set": "Primary notification sound updated",
- "notification_sound_primary_set_failed": "Failed to set primary notification sound",
- "notification_sound_preview_failed": "Could not play notification sound. Try again after interacting with the app.",
- "last_announced": "Last announced: {time}",
- "last_announced_never": "Last announced: Never",
- "display_name_placeholder": "Display Name",
- "profile": "Profile",
- "manage_identity": "Manage your identity, transport participation and LXMF defaults.",
- "theme": "Theme",
- "theme_mode": "{mode} mode",
- "transport": "Transport",
- "enabled": "Enabled",
- "disabled": "Disabled",
- "propagation": "Propagation",
- "local_node_running": "Local node running",
- "client_only": "Client-only",
- "copy": "Copy",
- "appearance": "Appearance",
- "appearance_description": "Switch between light and dark presets anytime.",
- "messages_sidebar_position": "Messages sidebar",
- "messages_sidebar_position_left": "Left",
- "messages_sidebar_position_right": "Right",
- "ui_transparency": "Background transparency",
- "ui_transparency_description": "Increases transparency on the main background. Side panels stay solid for readability.",
- "ui_glass_enabled": "Glass effect",
- "ui_glass_enabled_description": "Adds a frosted blur effect to cards. When off, cards use solid colors.",
- "messages_multi_pane_enabled": "Messages split view",
- "messages_multi_pane_enabled_description": "Show multiple conversations side by side on larger screens. Drag a conversation onto a pane to open it there.",
- "nomad_tabs_enabled": "NomadNet browser tabs",
- "nomad_tabs_enabled_description": "Open multiple NomadNet pages in tabs on larger screens.",
- "rrc_enabled": "Relay Chat",
- "rrc_enabled_description": "Enable relay chat, hub discovery, and hosting. When off, relay chat is hidden and hub announces are ignored.",
- "rrc_unread_badges_enabled": "Relay Chat unread badges",
- "rrc_unread_badges_enabled_description": "Show unread message counters on hubs and rooms. Mentions in the nav badge are unaffected.",
- "reset_appearance_defaults": "Reset appearance to defaults",
- "light_theme": "Light Theme",
- "dark_theme": "Dark Theme",
- "live_preview": "Live preview updates instantly.",
- "realtime": "Realtime",
- "transport_mode": "Transport Mode",
- "transport_description": "Relay paths and traffic for nearby peers.",
- "enable_transport_mode": "Enable Transport Mode",
- "transport_toggle_description": "Route announces, respond to path requests and help your mesh stay online.",
- "share_reticulum_instance": "Share Reticulum Instance",
- "share_reticulum_instance_description": "Make this MeshChatX Reticulum instance available to other local programs (Sideband-style shared instance). Other apps can use connectivity without Transport enabled.",
- "obfuscate_hops": "Obfuscate Hops",
- "obfuscate_hops_description": "When enabled, Reticulum applies a random local hop delta (RNS local_hops_delta) so outbound traffic does not advertise a true zero-hop origin to the wider mesh.",
- "respond_to_probes": "Respond to Probes",
- "respond_to_probes_description": "Allow this instance to answer Reticulum probe requests from the network.",
- "enable_remote_management": "Enable Remote Management",
- "enable_remote_management_description": "Allow remote management of this Reticulum instance when configured in the RNS config.",
- "remote_management_allowed": "Remote management allow-list",
- "remote_management_allowed_description": "Identity hashes allowed to query this instance remotely with rnstatus/rnpath. One 32-character hex hash per line.",
- "remote_management_allowed_placeholder": "9fb6d773498fb3feda407ed8ef2c3229",
- "remote_management_allowed_save": "Save allow-list",
- "shared_instance_type": "Shared Instance Type",
- "shared_instance_type_default": "Platform default",
- "shared_instance_type_description": "Use TCP when domain sockets are unavailable (common on Android). Leave default otherwise.",
- "instance_name": "Instance Name",
- "instance_name_description": "Isolate multiple shared instances on one system. Default is usually fine.",
- "rpc_config": "RPC Access",
- "rpc_config_description": "Copy these lines into another program's [reticulum] section for full shared-instance RPC access (management, interface status, path info).",
- "rpc_config_unavailable": "RPC key is not available yet. Start Reticulum as a shared instance first.",
- "rpc_config_copied": "RPC config copied to clipboard",
- "rpc_key_hidden": "Hidden — tap to show",
- "rpc_key_show": "Show RPC key",
- "rpc_key_hide": "Hide RPC key",
- "copy_rpc_config": "Copy RPC Config",
- "connected_to_shared_instance": "This MeshChatX process is attached to an external shared Reticulum instance. Edit that instance's config to change share/RPC settings.",
- "copy_failed": "Failed to copy to clipboard",
- "requires_restart": "Requires restart after toggling.",
- "show_community_interfaces": "Show Community Interfaces",
- "community_interfaces_description": "Show community-maintained presets when adding new interfaces.",
- "refresh_community_interfaces": "Refresh from directory.rns.recipes",
- "refresh_community_interfaces_busy": "Refreshing…",
- "reliability": "Reliability",
- "lxmf_settings_eyebrow": "LXMF",
- "privacy_eyebrow": "Privacy",
- "privacy_data_title": "Data & device",
- "privacy_data_description": "Manage local message history, telemetry, and other privacy options. Deleting here only affects this device.",
- "privacy_data": "Data & device",
- "privacy_subsection_device": "This device",
- "privacy_subsection_telemetry": "Mesh telemetry",
- "privacy_mode_enabled": "Privacy mode (block external HTTP/HTTPS)",
- "privacy_mode_description": "Blocks map tiles, geocoding, LibreTranslate, firmware downloads, and other outbound HTTP/HTTPS from the app. The browser Content Security Policy is tightened to same-origin only.",
- "multi_session_warning_enabled": "Warn when multiple sessions are connected",
- "multi_session_warning_description": "Show a warning toast when two or more browsers or devices are connected to this MeshChatX instance at the same time.",
- "multi_session_warning": "Multiple active sessions ({count}). Another browser or device is connected to this MeshChatX instance.",
- "screen_security_enabled": "Screen security (block capture)",
- "screen_security_description": "Uses a Windows display-affinity flag so MeshChatX is omitted from screenshots, screen recording, and Windows Recall. The window still looks normal on your monitor.",
- "screen_security_description_short": "Hide this window from screenshots, recorders, and Windows Recall.",
- "screen_security_drm_eyebrow": "Windows DRM / screen capture protection",
- "screen_security_drm_note": "This reuses the same OS content-protection flag media apps use for DRM. It is not a hard guarantee: a camera pointed at the display can still capture content, and some accessibility tools may need this setting off.",
- "screen_security_disable_confirm": "Turn off screen security? Windows Recall and other apps will be able to capture MeshChatX again.",
- "screen_security_enabled_toast": "Screen security enabled",
- "screen_security_disabled_toast": "Screen security disabled",
- "web_exposure_title": "Network exposure",
- "web_exposure_description": "The web UI bind address is set at startup via --host or MESHCHAT_HOST. Restrict who can reach it when not bound to loopback.",
- "web_listen_address": "Current bind address",
- "web_listen_https": "HTTPS",
- "web_exposure_warning_title": "This instance is reachable beyond localhost",
- "web_exposure_warning_body": "Binding to a non-loopback address exposes the web UI on your network. Enable authentication, restrict the port with a firewall, and prefer access over a VPN you control.",
- "web_exposure_check_auth": "Authentication enabled",
- "web_exposure_check_auth_off": "Authentication is disabled",
- "web_exposure_check_firewall": "I restricted port access with a firewall or bind rules",
- "web_exposure_check_vpn": "Remote access is only via a VPN I control",
- "web_ui_ip_allowlist": "Web UI IP allowlist",
- "web_ui_ip_allowlist_description": "Optional comma-separated IPs or CIDR ranges (for example 127.0.0.1/32, 192.168.1.0/24). Empty allows all clients.",
- "web_ui_ip_allowlist_placeholder": "127.0.0.1/32, ::1/128, 192.168.0.0/16",
- "landlock_status": "Landlock sandbox (Linux)",
- "landlock_active": "Active",
- "landlock_inactive": "Not active",
- "landlock_auto_enabled": "Enabled automatically on this Linux kernel",
- "landlock_kernel_unsupported": "Kernel does not support Landlock (5.13+ required)",
- "landlock_disabled_by_env": "Disabled via MESHCHAT_LANDLOCK=0",
- "seccomp_status": "Seccomp-BPF sandbox (Linux)",
- "seccomp_active": "Active",
- "seccomp_inactive": "Not active",
- "seccomp_auto_enabled": "Enabled automatically when libseccomp is available",
- "seccomp_kernel_unsupported": "Seccomp-BPF unavailable (install libseccomp, or unsupported platform)",
- "seccomp_disabled_by_env": "Disabled via MESHCHAT_SECCOMP=0",
- "settings_map_eyebrow": "Map",
- "messages_description": "Configure how MeshChat handles message delivery failures. Control automatic retry behavior, attachment retransmission, and fallback mechanisms to ensure reliable message delivery across the mesh network.",
- "auto_resend_title": "Auto resend when peer announces",
- "auto_resend_description": "Failed messages automatically retry once the destination broadcasts again.",
- "retry_attachments_title": "Allow retries with attachments",
- "retry_attachments_description": "Large payloads will also be retried (useful when both peers have high limits).",
- "auto_fallback_title": "Auto fall back to propagation node",
- "auto_fallback_description": "Failed direct deliveries are queued on your preferred propagation node.",
- "inbound_stamps_required_title": "Require inbound stamps",
- "inbound_stamps_required_description": "When off, direct messages to you do not require proof-of-work stamps. When on, set the stamp cost below. Higher values mean more work for senders.",
- "inbound_stamp_cost": "Inbound Message Stamp Cost",
- "inbound_stamp_description": "Proof-of-work difficulty for direct messages. Range: 1-254. Default: 8.",
- "flood_protection": "Flood Protection",
- "flood_protection_description": "Automatically raise the inbound stamp cost when receiving too many messages per minute from many sources. This makes coordinated spam attacks computationally expensive while keeping normal conversations affordable.",
- "flood_protection_enabled": "Enable auto-adjusting stamp cost",
- "flood_threshold": "Messages per minute threshold",
- "flood_max_stamp_cost": "Maximum stamp cost during flood",
- "flood_cooldown": "Cooldown before lowering cost (seconds)",
- "local_message_auto_delete_title": "Delete old messages on this device",
- "local_message_auto_delete_description": "Removes messages from the local database after the age you set. This only affects this device and no data is deleted from the network.",
- "local_message_auto_delete_age": "Delete messages older than",
- "local_message_auto_delete_unit_aria": "Time unit for message retention",
- "local_message_auto_delete_unit_days": "days",
- "local_message_auto_delete_unit_months": "months (30 days each)",
- "local_message_auto_delete_month_note": "Months are counted as 30 days each. Disabled by default.",
- "browse_nodes": "Browse Nodes",
- "propagation_nodes_description": "Keep conversations flowing even when peers are offline.",
- "nodes_info_1": "Propagation nodes hold messages securely until recipients sync again.",
- "nodes_info_2": "Nodes peer with each other to distribute encrypted payloads.",
- "nodes_info_3": "Most nodes retain data ~30 days, then discard undelivered items.",
- "run_local_node": "Run a local propagation node",
- "run_local_node_description": "MeshChat will announce and maintain a node using this local destination hash.",
- "preferred_propagation_node": "Preferred Propagation Node",
- "preferred_node_placeholder": "Destination hash, e.g. a39610c89d18bb48c73e429582423c24",
- "fallback_node_description": "Messages fallback to this node whenever direct delivery fails.",
- "auto_select_node": "Auto-select best node",
- "auto_select_node_description": "Automatically find and switch to the best available propagation node based on network hops.",
- "auto_select_using_label": "Using propagation node",
- "auto_select_pending": "No propagation node selected yet. The app will choose one when available.",
- "incoming_message_size": "Incoming message size",
- "incoming_message_size_description": "Maximum size for one direct LXMF delivery you will accept.",
- "incoming_message_size_1mb": "1 MB",
- "incoming_message_size_10mb": "10 MB",
- "incoming_message_size_25mb": "25 MB",
- "incoming_message_size_50mb": "50 MB",
- "incoming_message_size_1gb": "1 GB",
- "incoming_message_size_custom": "Custom",
- "incoming_message_size_unit_mb": "MB",
- "incoming_message_size_unit_gb": "GB",
- "auto_sync_interval": "Auto Sync Interval",
- "last_synced": "Last synced {time} ago.",
- "last_synced_never": "Last synced: never.",
- "propagation_stamp_cost": "Propagation Node Stamp Cost",
- "propagation_stamp_description": "Require proof-of-work stamps for messages sent through your node. Higher values require more work. Range: 13-254. Default: 16. **Note:** Changing this requires a restart.",
- "propagation_sequential_validation": "Sequential PN Stamp Validation",
- "propagation_sequential_validation_description": "Validate inbound propagation sync stamp batches one at a time. Other peers receive a throttle response while a batch is validating. Disable only on fast hosts.",
- "propagation_static_peers_bypass_sequential": "Static Peers Bypass Sequential Validation",
- "propagation_static_peers_bypass_sequential_description": "Allow configured static peers to sync immediately even when another stamp batch is already validating.",
- "propagation_max_inbound_syncs": "Max Concurrent Inbound Syncs",
- "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3.",
- "language": "Language",
- "select_language": "Select your preferred language.",
- "tagline": "All-in-one Reticulum client",
- "open": "Open",
- "identity": "Identity",
- "lxmf_address_hash": "LXMF Address Hash",
- "propagation_node_status": "Propagation Node Status",
- "last_sync": "Last Sync: {time} ago",
- "last_sync_never": "Last Sync: never",
- "syncing": "Syncing...",
- "synced": "Synced",
- "not_synced": "Not Synced",
- "not_configured": "Not Configured",
- "toggle_source": "Toggle Source Code",
- "audio_calls": "Telephone",
- "calls": "Calls",
- "status": "Status",
- "active_call": "Active Call",
- "incoming": "Incoming",
- "outgoing": "Outgoing",
- "call": "Call",
- "calls_plural": "Calls",
- "hop": "hop",
- "hops_plural": "hops",
- "hung_up_waiting": "Hung up, waiting for call...",
- "view_incoming_calls": "View Incoming Calls",
- "hangup_all_calls": "Hangup all Calls",
- "clear_history": "Clear History",
- "no_active_calls": "No active calls",
- "incoming_call": "Incoming call...",
- "outgoing_call": "Outgoing call...",
- "call_active": "Active",
- "call_ended": "Ended",
- "propagation_node": "Propagation Node",
- "sync_now": "Sync Now",
- "setting_auto_saved": "Setting {label} auto-saved",
- "backup_max_count": "Automatic backup limit",
- "message_font_size": "Message font size",
- "message_icon_size": "Icon size",
- "detailed_outbound_send_status": "Detailed outbound send status",
- "detailed_outbound_send_status_description": "Show detailed status like pathfinding, stamp solving, and technical tooltips while sending. When off, a simple checkmark is shown.",
- "outbound_transfer_progress_enabled": "Show transfer progress bar",
- "outbound_transfer_progress_enabled_description": "Show a progress bar with transfer speed, hops, and elapsed time below outbound messages while they are sending.",
- "message_timestamp_grouping": "Group message timestamps",
- "message_timestamp_grouping_description": "When on, messages are grouped by date and timestamps are simplified. When off, every message shows its exact time.",
- "search_settings": "Search settings...",
- "auto_resend": "Auto Resend",
- "retry_attachments": "Retry Attachments",
- "auto_fallback": "Auto Fallback",
- "community_interfaces": "Community Interfaces",
- "preferred_node": "Preferred Node",
- "local_node": "Local Node",
- "auto_sync": "Auto Sync",
- "inbound_stamp_cost_label": "Inbound Stamp Cost",
- "propagation_stamp_cost_label": "Propagation Stamp Cost",
- "page_archiver": "Page Archiver",
- "smart_crawler": "Smart Crawler",
- "authentication": "Authentication",
- "stranger_protection": "Stranger Protection",
- "stranger_protection_description": "Control how the app handles messages and attachments from unknown peers.",
- "block_stranger_attachments": "Block Attachments from Strangers",
- "block_stranger_attachments_description": "Automatically remove file, image, and audio attachments from people not in your contacts. Text messages are still delivered.",
- "block_all_from_strangers": "Block Everything from Strangers",
- "block_all_from_strangers_description": "Ignore all messages, attachments, and calls from people not in your contacts. Nothing is delivered or saved.",
- "show_unknown_contact_banner": "Show Unknown Contact Banner",
- "show_unknown_contact_banner_description": "When enabled, a notice appears at the top of conversations with peers who are not in your contacts.",
- "warn_on_stranger_links": "Warn Before Opening Stranger Links",
- "warn_on_stranger_links_description": "Show a warning prompt before opening HTTP/HTTPS links sent by peers who are not in your contacts.",
- "banishment": "Banishment",
- "banishment_description": "Customize the visual effect shown for banished peers and nodes.",
- "banished_effect_enabled": "Enable Banishment Effect",
- "banished_effect_description": "Show a visual overlay on banished peers and nodes.",
- "banished_text_label": "Banishment Text",
- "banished_text_description": "Custom message to display on the overlay.",
- "banished_color_label": "Banishment Color",
- "banished_color_description": "The color used for the banishment effect.",
- "desktop_open_calls_in_separate_window": "Open Calls in 2nd Window",
- "desktop_open_calls_in_separate_window_description": "When a call is active, it will be shown in a separate pop-out window rather than inside the app.",
- "desktop_hardware_acceleration_enabled": "Hardware Acceleration",
- "desktop_hardware_acceleration_enabled_description": "Disable this if you experience flickering, black screens, or other graphical issues.",
- "desktop_tray_enabled": "System tray integration",
- "desktop_tray_enabled_description": "Keep a tray icon so MeshChatX can hide to the background instead of quitting.",
- "desktop_close_behavior": "When closing the window",
- "desktop_close_behavior_description": "Choose whether closing the window quits, hides to the tray/taskbar, or asks each time.",
- "desktop_close_behavior_ask": "Ask every time",
- "desktop_close_behavior_quit": "Quit application",
- "desktop_close_behavior_background": "Keep running in background",
- "desktop_close_behavior_background_no_tray": "Minimize to taskbar",
- "translator": "Translator",
- "translator_enabled": "Enable Translator",
- "translator_description": "Enable translation features in conversations.",
- "libretranslate_url": "LibreTranslate URL",
- "libretranslate_url_description": "The URL where LibreTranslate API is accessible.",
- "system": "System",
- "reticulum_stack": "Reticulum Stack",
- "reticulum_stack_description": "Manage the underlying Reticulum Network Stack.",
- "reload_rns": "Reload RNS",
- "reload_rns_description": "Deregister all destinations, stop the current RNS instance, and reload it. This is useful for applying configuration changes or restarting the mesh network stack without restarting the entire application.",
- "reloading_rns": "Reloading RNS...",
- "restart_rns": "Restart RNS",
- "reloaded_rns": "Reticulum reloaded successfully",
- "announce_interval": "Announce Interval",
- "stop_sync_confirm": "Are you sure you want to stop syncing?",
- "cancel_inbound_deliveries": "Cancel incoming transfers",
- "cancel_inbound_deliveries_count": "Cancel incoming ({count})",
- "cancel_inbound_confirm": "Cancel {count} incoming LXMF delivery transfer(s)? Large messages currently downloading will stop.",
- "cancel_inbound_done": "Cancelled {count} incoming delivery transfer(s).",
- "cancel_inbound_failed": "Failed to cancel incoming deliveries",
- "sync_error_generic": "Something went wrong. Try again later.",
- "sync_complete": "Sync complete. {count} messages received.",
- "sync_error": "Sync error: {status}",
- "propagation_sync_live": "Syncing: {status} ({progress}%)",
- "propagation_sync_state": {
- "idle": "Idle",
- "path_requested": "Requesting path",
- "link_establishing": "Establishing link",
- "link_established": "Link established",
- "request_sent": "Request sent",
- "receiving": "Receiving messages",
- "response_received": "Processing response",
- "complete": "Complete",
- "no_path": "No path to node",
- "link_failed": "Link failed",
- "transfer_failed": "Transfer failed",
- "no_identity_received": "No identity received",
- "no_access": "Access denied",
- "failed": "Failed",
- "path_timeout": "Path timed out",
- "unknown": "Unknown state"
- },
- "switching_identity": "Switching Identity...",
- "loading_identity": "Loading your identity",
- "loading_overlay_kicker": "One moment",
- "loading_overlay_title": "Switching profile",
- "loading_overlay_subtitle": "Loading your chats and settings. This usually takes a few seconds.",
- "emergency_mode_active": "Emergency Mode Active - Using in-memory database and restricted services.",
- "backend_disconnected": "Disconnected from backend",
- "backend_process_stopped": "Reticulum backend stopped",
- "backend_reconnected": "Reconnected to backend",
- "restart_backend": "Restart backend",
- "network_starting": "Connecting to the mesh…",
- "network_degraded": "Mesh network unavailable. The app is still running so you can fix interfaces without wiping data.",
- "recover_network": "Retry network",
- "open_interfaces": "Open interfaces",
- "network_recovered": "Network stack recovered",
- "network_recover_failed": "Could not recover the network stack. Check interfaces and try again.",
- "restart_backend_started": "Restarting backend…",
- "restart_backend_failed": "Could not restart backend",
- "memory_pressure_warning": "High memory use detected. Close large Nomad pages, voice notes, or downloads if you can, then dismiss this warning. Persistent pressure can come from hostile peer content.",
- "view_backend_logs": "View crash log",
- "view_backend_logs_failed": "No crash log available",
- "blackhole_integration_enabled": "Blackhole Integration",
- "blackhole_integration_description": "Automatically blackhole identities at the Reticulum transport layer when banishing users in MeshChatX.",
- "announce_limits": "Announce Limits",
- "announce_limits_description": "Limit how many announcements are kept in the database and how many are loaded at once. Clear a field and save to restore defaults.",
- "announce_store_heading": "Store incoming announces",
- "announce_store_description": "When off, received announces of this type are not saved. This only affects storage and does not stop the network or your own announces. Your local NomadNet node will still be listed.",
- "announce_store_lxmf": "LXMF (lxmf.delivery)",
- "announce_store_lxst": "LXST telephony (lxst.telephony)",
- "announce_store_nomad": "NomadNet nodes (nomadnetwork.node)",
- "announce_store_prop": "Propagation nodes (lxmf.propagation)",
- "announce_max_stored_heading": "Max stored per aspect (database)",
- "announce_fetch_limit_heading": "Default page size (API)",
- "announce_search_max_fetch": "Max announces for search",
- "announce_search_max_fetch_hint": "Maximum rows read from the database when you use text search on announces (per request).",
- "discovered_interfaces_max_return": "Discovered interfaces cap",
- "discovered_interfaces_max_return_hint": "Maximum discovered and active interface entries returned to the UI.",
- "announce_limit_lxmf": "LXMF",
- "announce_limit_nomadnet": "NomadNet",
- "announce_limit_prop": "Prop Nodes",
- "csp_settings": "Content Security Policy (CSP)",
- "csp_description": "Manually add allowed domains for various resources. This is useful for custom map servers, bots, or external integrations.",
- "csp_extra_connect_src": "Extra Connect Sources",
- "csp_extra_connect_src_description": "Additional sources for fetch/websocket connections (e.g. https://api.example.com).",
- "csp_extra_img_src": "Extra Image Sources",
- "csp_extra_img_src_description": "Additional sources for images and tiles.",
- "csp_extra_frame_src": "Extra Frame Sources",
- "csp_extra_frame_src_description": "Additional sources for iframes.",
- "csp_extra_script_src": "Extra Script Sources",
- "csp_extra_script_src_description": "Additional sources for scripts (advanced use only).",
- "csp_extra_style_src": "Extra Style Sources",
- "csp_extra_style_src_description": "Additional sources for stylesheets.",
- "location": "Location",
- "location_source": "Location Source",
- "location_source_disabled": "Disabled",
- "location_source_disabled_desc": "Location sharing is turned off. Choose automatic or manual to share your coordinates.",
- "location_source_browser": "Automatic (Browser)",
- "location_source_manual": "Manual",
- "location_source_browser_desc": "Uses your browser's geolocation API. Note: In the desktop app, this may use Google services.",
- "location_source_manual_desc": "Use manually entered coordinates for maximum privacy.",
- "location_manual_lat": "Latitude",
- "location_manual_lon": "Longitude",
- "location_manual_alt": "Altitude (m)",
- "telemetry_enabled": "Telemetry Enabled",
- "telemetry_description": "Allow MeshChatX to send and receive telemetry data, such as responding to location requests from trusted peers.",
- "telemetry_trusted_peers": "Trusted Telemetry Peers",
- "telemetry_no_trusted_peers": "No trusted peers configured. Trust peers from the chat menu to allow them to request your location.",
- "telemetry_revoke_trust": "Revoke Trust",
- "telemetry_trust_revoked": "Revoked telemetry trust for {name}",
- "telemetry_trust_granted_alert": "Peer is now trusted for telemetry.",
- "telemetry_trust_revoked_alert": "Telemetry trust revoked.",
- "telemetry_trust_failed": "Failed to update telemetry trust.",
- "telemetry_trust_revoke": "Revoke Telemetry Trust",
- "telemetry_trust_grant": "Trust for Telemetry",
- "location_manage_desc": "Manage how your location is shared.",
- "map_settings_title": "Map",
- "map_settings_desc": "Map defaults, tiles, offline mode, and remote overlay limits.",
- "map_defaults_heading": "Map defaults",
- "map_tiles_heading": "Tiles and offline",
- "map_overlay_limits_heading": "Remote overlay limits",
- "map_overlay_limits_desc": "Guards for NomadNet and RNGit KMZ/KML/GeoJSON imports. Values are clamped to safe ranges.",
- "map_overlay_max_bytes": "Max overlay file size (bytes)",
- "map_overlay_max_features": "Max features per overlay",
- "map_overlay_max_kmz_uncompressed_bytes": "Max KMZ uncompressed size (bytes)",
- "map_overlay_max_sources": "Max overlay sources",
- "map_overlay_max_concurrent_jobs": "Max concurrent overlay jobs",
- "map_overlay_path_timeout_seconds": "Path lookup timeout (seconds)",
- "map_overlay_transfer_timeout_seconds": "Transfer timeout (seconds)",
- "map_overlay_job_timeout_seconds": "Job timeout (seconds)",
- "map_overlay_max_retries": "Max fetch retries",
- "map_overlay_retry_delay_seconds": "Retry base delay (seconds)",
- "map_default_lat": "Default latitude",
- "map_default_lon": "Default longitude",
- "map_default_zoom": "Default zoom",
- "map_tile_server_url": "Tile server URL",
- "map_nominatim_api_url": "Nominatim API URL",
- "map_offline_enabled": "Offline MBTiles enabled",
- "map_tile_cache_enabled": "Tile cache enabled",
- "battery_unavailable": "Battery status unavailable",
- "battery_level_title": "Battery {percent}",
- "battery_charging_title": "Charging {percent}",
- "battery_discharging_title": "On battery {percent}"
- },
- "common": {
- "open": "Open",
- "close": "Close",
- "cancel": "Cancel",
- "select": "Select",
- "save": "Save",
- "block": "Banish",
- "delete": "Delete",
- "edit": "Edit",
- "add": "Add",
- "sync": "Sync",
- "restart_app": "Restart App",
- "reveal": "Reveal",
- "refresh": "Refresh",
- "copy": "Copy",
- "copy_to_clipboard": "Copy to clipboard",
- "vacuum": "Vacuum",
- "auto_recover": "Auto Recover",
- "shutdown": "Shutdown",
- "acknowledge_reset": "Acknowledge & Reset",
- "continue": "Continue",
- "confirm": "Confirm",
- "delete_confirm": "Are you sure you want to delete this? This cannot be undone.",
- "search": "Search tools...",
- "no_results": "No tools found",
- "error": "Error",
- "success": "Success",
- "warning": "Warning",
- "info": "Info",
- "copied": "Copied to clipboard",
- "failed_to_copy": "Failed to copy to clipboard",
- "save_failed": "Failed to save configuration!",
- "invalid_address": "Invalid Address",
- "loading": "Loading...",
- "ok": "OK",
- "clear": "Clear",
- "prompt_title": "Enter a value"
- },
- "stickers": {
- "settings_title": "Stickers",
- "settings_description": "Your saved images for this identity. Use them in messages or save images from chats. You can also export or import a backup.",
- "export": "Export stickers",
- "import": "Import stickers",
- "export_done": "Stickers exported",
- "replace_duplicates": "Replace duplicate images when importing",
- "count": "{count} saved sticker(s)",
- "open_picker": "Stickers",
- "picker_tooltip": "Emoji, stickers, and GIFs",
- "tab_emojis": "Emojis",
- "tab_stickers": "Stickers",
- "empty_library": "No stickers yet.",
- "drop_or_click_hint": "Drop images here or click to browse (PNG, JPEG, GIF, WebP, BMP, TGS, WebM, max 512 KB each).",
- "add_more_hint": "Drop or click to add more stickers.",
- "file_too_large": "Each sticker must fit within its format limit (TGS 64 KB, WebM 256 KB, image 512 KB).",
- "unsupported_type": "Use PNG, JPEG, GIF, WebP, BMP, TGS, or WebM.",
- "uploaded_count": "Added {count} sticker(s).",
- "save_to_library": "Save image to stickers",
- "saved": "Sticker saved",
- "duplicate": "Already in your library",
- "save_failed": "Could not save sticker",
- "import_success": "Imported {imported}, skipped {skipped_duplicates} duplicates, invalid {skipped_invalid}",
- "import_failed": "Failed to import stickers",
- "all_stickers": "All stickers",
- "unpacked": "Unpacked",
- "open_editor": "Open sticker editor",
- "upload_short": "Upload",
- "format_static": "Static",
- "format_animated": "Animated",
- "format_video": "Video",
- "format_legacy": "Legacy",
- "errors": {
- "file_too_large_static": "Static stickers must be 512 KB or smaller.",
- "file_too_large_animated": "Animated TGS stickers must be 64 KB or smaller.",
- "file_too_large_video": "Video WEBM stickers must be 256 KB or smaller.",
- "invalid_dimensions": "Stickers must be at most 512x512 pixels with one side exactly 512.",
- "invalid_duration": "Animated and video stickers must be 3 seconds or shorter.",
- "invalid_fps": "Animated stickers must be 30-60 FPS, video stickers up to 30 FPS.",
- "invalid_codec": "Video stickers must use the VP9 codec.",
- "invalid_audio": "Video stickers must not contain an audio stream.",
- "invalid_tgs": "Could not read TGS animation.",
- "invalid_webm": "Could not read WebM video."
- },
- "packs": {
- "title": "Sticker packs",
- "description": "Group stickers into packs you can share or import.",
- "create": "New pack",
- "create_pack": "Create sticker pack",
- "title_label": "Title",
- "title_placeholder": "My pack",
- "short_name_label": "Short name",
- "short_name_placeholder": "e.g. cats",
- "description_label": "Description",
- "type_label": "Type",
- "type_static": "Static",
- "type_animated": "Animated",
- "type_video": "Video",
- "type_mixed": "Mixed",
- "save": "Save",
- "cancel": "Cancel",
- "open_editor": "Add stickers",
- "export": "Export pack",
- "import": "Import pack",
- "delete": "Delete pack",
- "delete_confirm": "Delete this pack?",
- "delete_with_stickers": "Also delete stickers in this pack",
- "deleted": "Pack deleted",
- "created": "Pack created",
- "exported": "Pack exported",
- "installed": "Pack installed: imported {imported}, skipped {skipped}",
- "install_failed": "Failed to install pack",
- "empty": "No packs yet.",
- "count_one": "{count} sticker",
- "count_other": "{count} stickers",
- "stickers_in_pack": "Stickers in {title}",
- "no_stickers": "No stickers in this pack yet."
- },
- "editor": {
- "title": "Sticker editor",
- "open": "Open editor",
- "select_image": "Select image",
- "drop_image": "Drop an image, or click to browse",
- "reset": "Reset",
- "remove_background": "Remove background",
- "removing_background": "Removing background...",
- "white_stroke": "White stroke",
- "shadow": "Drop shadow",
- "stroke_size": "Stroke size",
- "scale": "Scale",
- "rotate": "Rotate",
- "offset_x": "Offset X",
- "offset_y": "Offset Y",
- "text_overlay": "Text overlay",
- "text_placeholder": "Add text...",
- "text_color": "Text color",
- "text_size": "Text size",
- "format": "Export format",
- "format_png": "PNG",
- "format_webp": "WebP",
- "quality": "Quality",
- "emoji": "Emoji",
- "emoji_placeholder": "Optional",
- "pack": "Pack",
- "no_pack": "No pack",
- "save": "Save sticker",
- "saved": "Sticker saved",
- "save_failed": "Could not save sticker",
- "current_size": "Current size: {size}",
- "size_too_large": "Result is larger than 512 KB. Try lowering quality or using PNG with simpler shapes."
- }
- },
- "sticker_editor": {
- "title": "Sticker editor",
- "create_button": "Create sticker",
- "processing": "Processing...",
- "canvas_hint": "Preview is scaled to fit. Export uses a 512×512 canvas.",
- "section_source": "Source",
- "choose_image": "Choose image",
- "section_transform": "Transform",
- "scale": "Scale",
- "rotation": "Rotation",
- "flip_h": "Flip horizontal",
- "flip_v": "Flip vertical",
- "reset_transform": "Reset transform",
- "section_effects": "Effects",
- "bg_removed": "Background removed",
- "remove_background": "Remove background",
- "white_stroke": "White stroke",
- "shadow": "Drop shadow",
- "section_overlay": "Text overlay",
- "overlay_placeholder": "Add text...",
- "font_size": "Font size",
- "overlay_y": "Vertical position",
- "text_color": "Text color",
- "stroke_color": "Stroke color",
- "section_meta": "Details",
- "name_placeholder": "Sticker name",
- "emoji_placeholder": "Emoji (optional)",
- "format": "Export format",
- "quality": "Quality",
- "size_label": "Size: {size} (max {limit})",
- "cancel": "Cancel",
- "save": "Save sticker",
- "image_load_failed": "Could not load image",
- "removing_background": "Removing background...",
- "bg_removal_failed": "Background removal failed",
- "too_large": "Exported file is larger than 512 KB",
- "saving": "Saving...",
- "saved": "Sticker saved",
- "save_failed": "Could not save sticker"
- },
- "sticker_packs": {
- "all": "All",
- "section_title": "Sticker packs",
- "section_description": "Group stickers into packs you can share or import.",
- "create": "New pack",
- "open_editor": "Add stickers",
- "install_from_file": "Install from file",
- "empty": "No packs yet.",
- "count_label": "{count} stickers",
- "add_sticker": "Add sticker",
- "export": "Export pack",
- "delete": "Delete pack",
- "empty_pack": "No stickers in this pack yet.",
- "create_title": "Create sticker pack",
- "field_title": "Title",
- "field_short_name": "Short name",
- "field_description": "Description",
- "type_static": "Static",
- "type_animated": "Animated",
- "type_video": "Video",
- "type_mixed": "Mixed",
- "strict_label": "Enforce pack type (reject mismatched uploads)",
- "created": "Pack created",
- "create_failed": "Could not create pack",
- "exported": "Pack exported",
- "export_failed": "Export failed",
- "confirm_delete": "Delete pack \"{title}\"?",
- "deleted": "Pack deleted",
- "delete_failed": "Could not delete pack",
- "installed": "Pack installed ({imported} imported)",
- "install_failed": "Could not install pack"
- },
- "gifs": {
- "settings_title": "GIFs",
- "settings_description": "Your personal GIF library. Upload GIF and animated WebP files to reuse them in chats. Your most-used GIFs are shown first.",
- "export": "Export GIFs",
- "import": "Import GIFs",
- "export_done": "GIFs exported",
- "replace_duplicates": "Replace duplicate GIFs when importing",
- "count": "{count} saved GIF(s)",
- "tab_gifs": "GIFs",
- "empty_library": "No GIFs yet. Upload a GIF or save one from a chat.",
- "drop_or_click_hint": "Drop GIFs here or click to browse (GIF or WebP, max 5 MB each).",
- "add_more_hint": "Drop or click to add more GIFs.",
- "file_too_large": "Each GIF must be 5 MB or smaller.",
- "unsupported_type": "Use GIF or WebP files only.",
- "uploaded_count": "Added {count} GIF(s).",
- "save_to_library": "Save image to GIFs",
- "saved": "GIF saved",
- "duplicate": "Already in your GIF library",
- "save_failed": "Could not save GIF",
- "import_success": "Imported {imported}, skipped {skipped_duplicates} duplicates, invalid {skipped_invalid}",
- "import_failed": "Failed to import GIFs"
- },
- "plugins": {
- "settings": {
- "title": "Plugins",
- "description": "Install, enable, and review permissions for MeshChatX plugins. ZIP and WASM installs show a confirmation dialog with permissions, signature status, and security findings.",
- "enable": "Enable",
- "disable": "Disable",
- "remove": "Remove",
- "permissions": "Permissions",
- "install_zip": "Install plugin from ZIP or WASM bundle",
- "enabled": "Plugin enabled",
- "disabled": "Plugin disabled",
- "removed": "Plugin removed",
- "installed": "Plugin installed",
- "auto_disabled": "Plugin auto-disabled: {reason}",
- "kill_switch": "A plugin was disabled: {reason}",
- "empty_state": "No plugins installed yet. Upload a plugin ZIP or WASM bundle to get started.",
- "drag_drop": "Drag and drop a plugin ZIP or WASM bundle here",
- "choose_file": "Choose ZIP or WASM file",
- "confirm_remove": "Remove plugin \"{name}\"? This deletes its files and stored data.",
- "badge_enabled": "Enabled",
- "badge_disabled": "Disabled",
- "badge_frontend": "UI",
- "badge_wasm": "WASM",
- "badge_python": "Python",
- "badge_signed": "Signed",
- "badge_trusted": "Trusted",
- "badge_invalid_signature": "Invalid signature",
- "badge_tampered": "Tampered",
- "install_failed": "Plugin install failed: {reason}",
- "installing": "Installing plugin...",
- "badge_network": "Network",
- "network_endpoints": "Network endpoints",
- "previewing": "Reviewing plugin..."
- },
- "permissions": {
- "none": "No elevated permissions",
- "hooks.announce.received": "Receive mesh announce events",
- "hooks.rns.link.event": "Receive RNS link packet and close events",
- "managers.destinationPath.read": "Read the Reticulum path table",
- "managers.debugLog.read": "Read application debug logs",
- "managers.bugReport.status": "Read bug report collector status",
- "managers.bugReport.listCollectors": "List heard mcx-bugs-v1 collectors",
- "managers.bugReport.listReports": "List received bug reports",
- "managers.bugReport.preview": "Preview redacted debug logs for bug reports",
- "managers.bugReport.send": "Send bug reports over the mesh",
- "managers.bugReport.startCollector": "Start an mcx-bugs-v1 collector",
- "managers.bugReport.stopCollector": "Stop the bug report collector",
- "managers.bugReport.announce": "Announce the bug report collector",
- "managers.rnsLink.open": "Open RNS links to destinations",
- "managers.rnsLink.identify": "Identify on RNS links",
- "managers.rnsLink.request": "Send request/response over RNS links",
- "managers.rnsLink.send": "Send packets over RNS links",
- "managers.rnsLink.close": "Close RNS links",
- "storage.isolated": "Use isolated plugin storage",
- "network.fetch": "Make outbound internet HTTP requests"
- },
- "install_dialog": {
- "title": "Install plugin",
- "network_title": "Install plugin with network access",
- "message": "Review permissions for \"{name}\" ({id}) before installing.",
- "permissions": "Requested permissions",
- "permissions_hint": "Uncheck any permission you do not want to grant. Denied capabilities stay unavailable at runtime.",
- "grant": "Allow",
- "network_endpoints": "External network endpoints",
- "network_endpoints_blocked": "Network access is not granted. These URLs will remain blocked.",
- "network_endpoints_unknown": "This plugin requests network access but did not declare specific endpoints.",
- "signature": "Signature",
- "signature_unsigned": "Unsigned",
- "signature_signed": "Signed",
- "signature_trusted": "Trusted publisher",
- "signature_invalid": "Invalid signature (install blocked)",
- "trust_publisher": "Trust this publisher",
- "security_findings": "Security findings",
- "cancel": "Cancel",
- "confirm": "Install",
- "close": "Close"
- },
- "sideband": {
- "title": "Sideband plugins",
- "description": "Optional Sideband-compatible Python plugins loaded from a directory. They run in-process with full host access when enabled.",
- "master_enable": "Enable Sideband plugin loader (dangerous)",
- "command_enable": "Enable Sideband command plugins",
- "path": "Plugin directory path",
- "browse": "Browse",
- "browse_title": "Choose Sideband plugins folder",
- "path_prompt": "Enter the full path to the Sideband plugins folder",
- "path_picked": "Plugin folder selected",
- "save": "Save Sideband settings",
- "reload": "Reload Sideband plugins",
- "loaded": "Loaded Sideband plugins",
- "saved": "Sideband plugin settings saved",
- "reloaded": "Sideband plugins reloaded",
- "danger_confirm": "Sideband plugins run as full Python code inside MeshChatX with filesystem, network, and LXMF access. Enable only if you trust every script in the directory."
- }
- },
- "selftest": {
- "title": "System Self-Test",
- "description": "Run diagnostic checks for the network stack, database, identity, imports, storage, LXMF, subprocess/run-module, SQLite, identity files, loopback TCP, unicode paths, RNode helpers, bot launcher argv, HTTP status/config/health/auth/bots/security/interfaces/reticulum-instance/identities/favourites/telephone/plugins/sideband/RRC APIs, plugin runtime, WebSocket, RNS Link API, and bot lifecycle.",
- "run_test_btn": "Run Diagnostics",
- "running": "Running Diagnostics...",
- "stack_up": "Network Stack",
- "config_good": "Configuration Integrity",
- "db_good": "Database Connection",
- "read_write": "Storage Read/Write",
- "identity_good": "Identity Loaded",
- "imports_good": "Critical Imports",
- "storage_lock_good": "Storage Lock",
- "temp_fs_good": "Temp Filesystem",
- "public_assets_good": "Public Assets",
- "lxmf_router_good": "LXMF Router",
- "subprocess_good": "Subprocess Spawn",
- "run_module_good": "MeshChatX Run-Module",
- "sqlite_roundtrip": "SQLite Roundtrip",
- "identity_roundtrip": "Identity File Roundtrip",
- "loopback_tcp": "Loopback TCP Bind",
- "unicode_path_good": "Unicode Path I/O",
- "rnode_support_good": "RNode Support Module",
- "bot_launcher_good": "Bot Launcher Argv",
- "http_status_good": "HTTP Status",
- "http_app_info_good": "HTTP App Info",
- "http_config_good": "HTTP Config",
- "http_db_health_good": "HTTP Database Health",
- "http_auth_csrf_good": "HTTP Auth CSRF",
- "http_bots_status_good": "HTTP Bots Status",
- "http_security_good": "HTTP Server Security",
- "http_interfaces_good": "HTTP RNS Interfaces",
- "http_reticulum_instance_good": "HTTP RNS Instance Settings",
- "http_identities_good": "HTTP Identities",
- "http_favourites_good": "HTTP Favourites",
- "http_telephone_good": "HTTP Telephone Status",
- "http_plugins_good": "HTTP Plugins API",
- "http_plugins_trust_good": "HTTP Plugin Trust List",
- "http_sideband_plugins_good": "HTTP Sideband Plugins",
- "http_sideband_config_good": "HTTP Sideband Config",
- "http_rrc_hubs_good": "HTTP RRC Hubs",
- "http_rrc_servers_good": "HTTP RRC Servers",
- "plugins_runtime_good": "Plugin Manager Runtime",
- "websocket_good": "WebSocket",
- "websocket_rns_link_good": "WebSocket RNS Link API",
- "bots_lifecycle": "Bot Create / Start / Stop / Delete",
- "passed": "Passed",
- "failed": "Failed",
- "expand_reason": "Show failure details",
- "collapse_reason": "Hide failure details",
- "status_label": "Status",
- "reason_label": "Reason",
- "checks_completed": "All checks passed successfully."
- },
- "maintenance": {
- "title": "Maintenance & Data",
- "description": "Manage your local data, clear caches, and backup conversations.",
- "purge_old_title": "Clean up old messages",
- "purge_old_desc": "Delete local messages older than a number of days or before a date. Attachments stored in those messages are removed too. Export an archive first if you may want to reimport later.",
- "purge_mode_days": "Older than days",
- "purge_mode_date": "Before date",
- "purge_older_than_days": "Delete messages older than (days)",
- "purge_before_date": "Delete messages before",
- "purge_preview": "Count matching",
- "purge_preview_loading": "Counting matching messages...",
- "purge_preview_hint": "Choose a filter, then count matching messages.",
- "purge_preview_count": "{count} messages match this filter",
- "purge_filter_invalid": "Enter a valid day count or date.",
- "export_old_archive": "Export matching archive",
- "export_old_archive_done": "Matching message archive downloaded",
- "purge_old_confirm_btn": "Delete matching",
- "purge_old_confirm": "Permanently delete matching local messages and their attachments? This cannot be undone.",
- "purge_old_done": "Deleted {count} messages",
- "clear_messages": "Clear All Messages",
- "clear_messages_desc": "Permanently delete all sent and received messages.",
- "clear_duplicates": "Clear Duplicate Messages",
- "clear_duplicates_desc": "Remove extra copies that share the same text in the same conversation. Keeps the oldest of each set.",
- "clear_duplicates_confirm": "Delete duplicate messages that match by content in each conversation? The oldest copy of each set is kept. This cannot be undone.",
- "clear_duplicates_done": "Removed {count} duplicate messages",
- "clear_announces": "Clear All Announces",
- "clear_announces_desc": "Remove all discovered peers and nodes from your cache.",
- "clear_nomadnet_favs": "Clear NomadNetwork Favorites",
- "clear_nomadnet_favs_desc": "Remove all saved NomadNetwork nodes from your favorites.",
- "clear_lxmf_icons": "Clear LXMF Icons",
- "clear_lxmf_icons_desc": "Remove cached LXMF user icons from your device.",
- "clear_stickers": "Clear All Stickers",
- "clear_stickers_desc": "Remove every saved sticker image for this identity.",
- "stickers_cleared": "Stickers cleared",
- "clear_gifs": "Clear All GIFs",
- "clear_gifs_desc": "Remove every saved GIF for this identity.",
- "gifs_cleared": "GIFs cleared",
- "clear_archives": "Clear Page Archives",
- "clear_archives_desc": "Delete all cached NomadNetwork pages.",
- "clear_reticulum_docs": "Clear Reticulum Docs",
- "clear_reticulum_docs_desc": "Removes all downloaded Reticulum documentation and versions.",
- "clear_path_table": "Clear Path Table",
- "clear_path_table_desc": "Drop all cached Reticulum transport paths. Routes will be re-discovered as needed.",
- "path_table_cleared": "Path table cleared",
- "export_messages": "Export Messages",
- "export_messages_desc": "Download a reimportable JSON archive (messages, attachments, contacts, read state).",
- "export_messages_done": "Message archive downloaded",
- "import_messages": "Import Messages",
- "import_messages_desc": "Restore conversations from a previously exported JSON archive.",
- "clear_confirm": "Are you sure? This action cannot be undone.",
- "messages_cleared": "Messages cleared successfully",
- "announces_cleared": "Announces cleared successfully",
- "favourites_cleared": "Favorites cleared successfully",
- "lxmf_icons_cleared": "LXMF icons cleared successfully",
- "archives_cleared": "Archives cleared successfully",
- "docs_cleared": "Reticulum documentation cleared successfully",
- "import_success": "Successfully imported {count} messages",
- "import_failed": "Failed to import messages",
- "export_nomadnet_favourites": "Export NomadNet favourites",
- "import_nomadnet_favourites": "Import NomadNet favourites",
- "nomadnet_favourites_exported": "NomadNet favourites layout exported",
- "nomadnet_favourites_export_failed": "Could not export NomadNet favourites",
- "nomadnet_favourites_imported": "NomadNet favourites layout imported",
- "nomadnet_favourites_import_failed": "Could not import NomadNet favourites file"
- },
- "identities": {
- "title": "Identities",
- "eyebrow": "Account",
- "manage": "Use different names on the same device. Switch anytime without reinstalling.",
- "active_identity": "Active identity",
- "other_identities": "Other identities",
- "show_addresses": "Show addresses",
- "hide_addresses": "Hide addresses",
- "lxst_address": "LXST address",
- "new_identity": "New Identity",
- "import": "Import",
- "import_hint": "Restore from a backup file or pasted backup text.",
- "import_key_only_hint": "This restores the identity key only. Message history and settings require a database backup zip from About.",
- "export_all": "Backup all",
- "export_all_success": "All identities backed up",
- "export_all_failed": "Failed to back up identities",
- "generate_fresh": "Create a new name and key pair for the mesh.",
- "display_name": "Display Name",
- "display_name_hint": "e.g. Work, Field radio, Personal",
- "current": "Active",
- "switch": "Switch to this identity",
- "switch_label": "Switch",
- "delete": "Delete identity",
- "no_identities": "No identities yet",
- "create_first": "Create an identity to start chatting on the mesh.",
- "switch_confirm": "Switch to \"{name}\"?",
- "switch_after_restore_confirm": "Switch to restored identity \"{name}\" now?",
- "delete_confirm": "Delete \"{name}\"? This cannot be undone.",
- "switched": "Identity switched successfully.",
- "created": "Identity created successfully",
- "deleted": "Identity deleted",
- "failed_load": "Failed to load identities",
- "enter_display_name_warning": "Please enter a display name",
- "failed_create": "Failed to create identity",
- "switch_scheduled": "Switch scheduled. Reloading application...",
- "failed_switch": "Failed to switch identity",
- "failed_delete": "Failed to delete identity",
- "key_control": "Backup",
- "export_key_file": "Export backup file",
- "copy_base32": "Copy backup text",
- "upload_key_file": "Upload backup file",
- "paste_base32": "Paste backup text",
- "paste_base32_placeholder": "Paste your identity backup text here...",
- "confirm_restore": "Confirm restore",
- "restoring": "Restoring...",
- "identity_exported": "Backup file exported",
- "identity_copied": "Backup text copied to clipboard",
- "identity_export_failed": "Failed to export backup",
- "identity_copy_failed": "Failed to copy backup",
- "identity_restored": "Identity restored.",
- "identity_restore_failed": "Identity restore failed",
- "identity_restore_empty_file": "Identity file is empty.",
- "identity_restore_file_too_large": "Identity file is too large.",
- "no_identity_available": "No identity available",
- "message_count": "{count} messages"
- },
- "about": {
- "title": "About",
- "third_party_licenses": "Third-party licenses",
- "version": "v{version}",
- "ui_build": "UI build {date}",
- "rns_version": "RNS {version}",
- "lxmf_version": "LXMF {version}",
- "python_version": "Python {version}",
- "config_path": "Config path",
- "database_path": "Database path",
- "database_size": "Database size",
- "active_sessions": "Active sessions",
- "active_sessions_description": "Browsers and devices currently connected to this MeshChatX instance over the UI WebSocket.",
- "active_sessions_empty": "No active sessions.",
- "active_sessions_count": "{count} active",
- "active_session_ip": "IP address",
- "active_session_user_agent": "User agent",
- "active_session_connected": "Connected",
- "database_health": "Database Health",
- "database_health_description": "Quick check, WAL tuning, and recovery tools for the MeshChatX database.",
- "running_checks": "Running checks...",
- "integrity": "Integrity",
- "journal_mode": "Journal mode",
- "wal_autocheckpoint": "WAL autocheckpoint",
- "page_size": "Page size",
- "pages_free": "Pages / Free",
- "free_space_estimate": "Free space estimate",
- "system_resources": "System Resources",
- "live": "Live",
- "memory_rss": "Memory (RSS)",
- "virtual_memory": "Virtual Memory",
- "network_stats": "Network Stats",
- "sent": "Sent",
- "received": "Received",
- "packets_sent": "Packets Sent",
- "packets_received": "Packets Received",
- "reticulum_stats": "Reticulum Stats",
- "total_paths": "Total Paths",
- "announces_per_second": "Announces / sec",
- "announces_per_minute": "Announces / min",
- "announces_per_hour": "Announces / hr",
- "download_activity": "Download Activity",
- "no_downloads_yet": "No downloads yet",
- "runtime_status": "Runtime Status",
- "shared_instance": "Shared Instance",
- "standalone_instance": "Standalone Instance",
- "transport_enabled": "Transport Enabled",
- "transport_disabled": "Transport Disabled",
- "identity_addresses": "Identity & Addresses",
- "telephone_address": "Telephone Address",
- "security_integrity": "Integrity Monitor",
- "security_integrity_description": "Advisory tamper-evidence. Detects unexpected changes to your identity and database files between startups. It is not tamper-proofing and does not protect against an attacker with access to this account.",
- "secured": "No changes detected",
- "tampering_detected": "Changes detected",
- "technical_issues": "Technical Issues:",
- "integrity_backend_error": "The application backend binary (unpacked from ASAR) appears to have changed since the last snapshot. If you did not update or modify it, review the change.",
- "integrity_data_error": "Your identity or database files appear to have changed while the app was closed.",
- "integrity_warning_footer": "This is advisory, not a confirmed compromise. If you updated the app or edited these files yourself, you can acknowledge to reset the baseline.",
- "no_integrity_violations": "No unexpected changes to monitored files since last startup.",
- "dependency_chain": "Stack versions",
- "app_name": "MeshChatX",
- "automatic_backups_desc": "Automated daily snapshots of your database.",
- "automatic_backups_title": "Automatic Backups",
- "backup_download_failed": "Failed to download backup",
- "backup_downloaded": "Backup downloaded",
- "backend_stack": "Python packages",
- "chrome_runtime": "Chrome",
- "contact_alternate": "Alternate",
- "contact_details": "Details",
- "contact_developer": "Contact Developer",
- "contact_support_title": "Contact & support",
- "contact_lxmf_address": "LXMF Address",
- "contact_propagation_hint": "Send to propagation node if you cant reach me!",
- "contact_open_messages": "Open conversation",
- "contact_copy_address": "Copy address",
- "donate_label": "Donate",
- "donate_monero_label": "Monero (XMR)",
- "donate_copy_monero": "Copy Monero address",
- "donate_kofi": "Ko-fi",
- "donate_buymeacoffee": "Buy Me a Coffee",
- "copied_label_to_clipboard": "{label} copied to clipboard",
- "core_runtime": "Runtime versions",
- "creating": "Creating...",
- "database_backups_desc": "Full snapshots of your communications database.",
- "database_backups_title": "Database Backups",
- "database_health_maintenance": "Database Health & Maintenance",
- "dep_lxmf_subtitle": "LXMF",
- "dep_lxmfy_subtitle": "LXMFy",
- "dep_rns_subtitle": "RNS",
- "download_backup": "Download Backup",
- "downloading": "Downloading...",
- "electron_runtime": "Electron",
- "environment_information": "Environment Information",
- "env_backend_url": "Backend URL",
- "env_language": "Language",
- "env_lxmf": "LXMF",
- "env_platform": "Platform",
- "env_python": "Python",
- "env_rns": "RNS",
- "env_user_agent": "User Agent",
- "failed_to_copy_label": "Failed to copy {label}",
- "free_space": "Free Space",
- "identity_hash": "Identity Hash",
- "journal_short": "Journal",
- "local_snapshots_desc": "Create point-in-time restore points on disk.",
- "local_snapshots_title": "Local Snapshots",
- "lxmf_address": "LXMF Address",
- "lxst_engine": "LXST",
- "main_instance_badge": "Main Instance",
- "nodejs_runtime": "Node.js",
- "page_count_label": "Page Count",
- "pagination_page_of": "Page {current} of {total}",
- "path_unknown": "unknown",
- "recovery": "Recovery",
- "recovery_complete": "Database recovery finished.",
- "recovery_confirm": "Run database recovery? This runs a full integrity check and compacts the database (VACUUM). It can take a while and the app may feel unresponsive. This does not load a backup file.",
- "recovery_failed": "Recovery failed",
- "vacuum_complete": "Database vacuum finished.",
- "vacuum_failed": "Could not vacuum the database.",
- "reveal_config_file": "Reveal File",
- "reveal_database_file": "Reveal DB",
- "reticulum_config": "Reticulum Config",
- "shared_instance_badge": "Shared Instance: {address}",
- "shutdown_confirm": "Are you sure you want to shutdown the app? This will stop the server and close the application.",
- "snapshot_create": "Create",
- "snapshot_download": "Download",
- "snapshot_download_failed": "Failed to download snapshot",
- "snapshot_downloaded": "Snapshot downloaded",
- "snapshot_placeholder": "Snapshot label...",
- "snapshot_restore": "Restore",
- "tagline_after": ".",
- "tagline_lead": "A secure, resilient, and beautiful communications platform powered by the",
- "tagline_link": "Reticulum Network Stack",
- "technical_issues_detected": "Changes since last snapshot",
- "other_core_components": "Other Core Components",
- "backend_dependencies": "Backend Dependencies",
- "delete_snapshot_confirm": "Are you sure you want to delete this snapshot?",
- "snapshot_deleted": "Snapshot deleted",
- "failed_delete_snapshot": "Failed to delete snapshot",
- "delete_backup_confirm": "Are you sure you want to delete this backup?",
- "backup_deleted": "Backup deleted",
- "failed_delete_backup": "Failed to delete backup",
- "database_restored": "Database restored. Relaunching...",
- "failed_restore_snapshot": "Failed to restore snapshot",
- "restore_snapshot_confirm": "Are you sure you want to restore this snapshot? This will overwrite the current database and require an app relaunch.",
- "restore_from_file": "Restore from File",
- "restoring": "Restoring...",
- "restore_select_file": "Select a backup file to restore.",
- "restore_file_confirm": "Are you sure you want to restore from this backup file? This will overwrite the current database and require an app relaunch.",
- "failed_restore_file": "Failed to restore from file",
- "integrity_acknowledged": "Integrity issues acknowledged",
- "integrity_acknowledged_reset": "Integrity issues acknowledged and manifest reset",
- "integrity_acknowledge_confirm": "Are you sure you want to acknowledge these integrity issues? This will update the security manifest to match the current state of your files.",
- "failed_acknowledge_integrity": "Failed to acknowledge integrity issues",
- "shutdown_sent": "Shutdown command sent to server.",
- "identity_exported": "Identity key file exported",
- "identity_copied": "Identity Base32 key copied to clipboard",
- "env_battery": "Battery",
- "env_battery_unavailable": "Unavailable on this device",
- "env_battery_charging": "Charging {percent}",
- "env_battery_on_battery": "On battery {percent}",
- "usage_insights": "MeshChatX usage",
- "process_cpu": "Process CPU",
- "process_threads": "Threads",
- "process_uptime": "Uptime",
- "memory_pressure": "Memory pressure",
- "memory_pressure_relaxed": "Active (low host RAM)",
- "memory_pressure_normal": "Normal",
- "memory_pressure_paths": "{count} paths tracked",
- "path_table": "Path table",
- "path_table_count": "{count} paths",
- "env_host_battery": "Host battery",
- "app_battery_use": "Est. MeshChatX battery",
- "app_battery_use_hint": "Estimated from MeshChatX CPU time since start. Not an OS battery attribution.",
- "app_battery_use_warming": "Measuring…",
- "app_battery_use_rate": "~{rate}",
- "app_battery_use_with_intensity": "~{rate} ({intensity})",
- "app_battery_share": "Est. CPU share",
- "app_battery_share_value": "{percent}% of device",
- "app_battery_intensity_low": "low",
- "app_battery_intensity_moderate": "moderate",
- "app_battery_intensity_high": "high",
- "app_battery_intensity_very_high": "very high",
- "battery_saver": "Battery saver",
- "battery_saver_off": "Off",
- "battery_saver_on": "On",
- "battery_saver_measures": "Active measures",
- "battery_saver_measure": {
- "disableVisualiserDiscovery": "Visualiser discovery hidden",
- "hideOfflineInterfaces": "Offline interfaces hidden",
- "maxVisualiserInterfaces": "Visualiser interface limit",
- "disableVisualiserAutoReload": "Visualiser auto-reload off",
- "slowVisualiserReload": "Slower visualiser reload",
- "disableVisualiserLiveLayout": "Live layout off",
- "reduceBackgroundPolling": "Slower background polling",
- "reduceInterfacesDiscovery": "Slower interface discovery polls",
- "applyInterfaceBitrateLimits": "Forced interface bitrates"
- },
- "top_memory_consumer": "Top memory",
- "top_cpu_consumer": "Top CPU"
- },
- "interfaces": {
- "title": "Interfaces",
- "manage": "Manage",
- "description": "Search, filter and export your Reticulum adapters.",
- "add_interface": "Add Interface",
- "edit_interface": "Edit Interface",
- "import": "Import",
- "export_all": "Export all",
- "search_placeholder": "Search by name, type, host...",
- "all": "All",
- "connected_only": "Connected only",
- "all_types": "All types",
- "no_interfaces_found": "No interfaces found",
- "no_interfaces_description": "Adjust your search or add a new interface.",
- "fixed_mtu_hint": "Optional. If set, must be at least {min} bytes (Reticulum minimum MTU).",
- "fixed_mtu_min": "Fixed MTU must be at least {min} bytes (Reticulum minimum).",
- "restart_required": "Restart required",
- "restart_description": "Reticulum MeshChatX must be restarted for any interface changes to take effect.",
- "restart_now": "Restart now",
- "failed_enable": "failed to enable interface",
- "failed_disable": "failed to disable interface",
- "delete_confirm": "Are you sure you want to delete this interface? This can not be undone!",
- "failed_delete": "failed to delete interface",
- "failed_export_all": "Failed to export interfaces",
- "failed_export_single": "Failed to export interface",
- "failed_reload": "Failed to reload Reticulum!",
- "interface_not_found": "The selected interface for editing could not be found.",
- "discovery_settings_saved": "Discovery settings saved",
- "discovery_list_refreshed": "Discovered interfaces refreshed.",
- "discovery_list_refresh_failed": "Could not refresh discovered interfaces.",
- "discovery_default_bootstrap_only": "Default bootstrap-only for new outbound TCP",
- "discovery_default_bootstrap_only_hint": "When enabled, new TCP client (and backbone connector) interfaces get bootstrap_only so Reticulum can detach them once autoconnect_discovered_interfaces is satisfied.",
- "discovery_default_bootstrap_docs_link": "See Reticulum bootstrapping connectivity.",
- "backbone_transport_identity_label": "Transport identity (optional)",
- "backbone_transport_identity_placeholder": "e53433e51cde34c42a3245ba3fe1ad69",
- "backbone_transport_identity_hint": "Matches Reticulum backbone remote examples that only set remote and target_port. Use the hash when an operator or directory specifies it.",
- "mode_default_full": "Default (Full)",
- "mode_full": "Full",
- "mode_gateway": "Gateway",
- "mode_access_point": "Access Point",
- "mode_roaming": "Roaming",
- "mode_boundary": "Boundary",
- "mode_internal": "Internal",
- "recursive_prs_label": "Recursive path requests",
- "recursive_prs_hint": "Allow recursive path discovery on this interface regardless of mode (RNS recursive_prs).",
- "announces_from_internal_label": "Propagate announces from internal interfaces",
- "announces_from_internal_hint": "When off, announces received on internal-mode interfaces are not rebroadcast on this interface (RNS announces_from_internal).",
- "location_cmd_label": "Location command (optional)",
- "location_cmd_placeholder": "/path/to/gps-script",
- "location_cmd_hint": "Absolute path to an executable that prints latitude,longitude,height. Reticulum runs it for discovery announces (RNS location_cmd).",
- "block_fast_flapping_label": "Block fast-flapping clients",
- "block_fast_flapping_hint": "Ignore clients that connect and disconnect too quickly (RNS 1.4.0 BackboneInterface defaults).",
- "fast_flapping_block_time_label": "Block time (minutes)",
- "fast_flapping_threshold_label": "Threshold (seconds)",
- "fast_flapping_grace_label": "Grace flaps",
- "loopback_local_title": "Local / loopback",
- "loopback_local_body": "Reticulum uses a shared instance internally. To talk to another RNS process on this host, use a TCP Client to 127.0.0.1 with the instance port, or use an external interface module in the interface path.",
- "loopback_local_docs_hint": "See the Interfaces chapter for supported types.",
- "loopback_local_docs_link": "Open bundled Reticulum manual",
- "custom_external_intro": "Types such as WeaveInterface or your own class are loaded when Reticulum finds a matching module file (TypeName.py) under interfacepath and exposes interface_class (see RNS source). Options below are merged into the interface stanza in your config file.",
- "custom_external_install_intro": "On Android, Android/data is often hidden from the Files app. Use Install module to copy a TypeName.py into this app interfacepath via the system file picker (Downloads, Drive, Bluetooth, etc.).",
- "custom_external_interfacepath_label": "Active interfacepath",
- "custom_external_install_button": "Install module (.py)",
- "custom_external_install_overwrite": "Overwrite if present",
- "custom_external_install_success": "Interface module installed",
- "custom_external_install_failed": "Could not install interface module",
- "custom_external_modules_empty": "No custom modules installed yet.",
- "custom_external_module_delete": "Delete",
- "custom_external_module_deleted": "Interface module deleted",
- "custom_external_module_delete_failed": "Could not delete interface module",
- "custom_external_type_label": "Interface type name",
- "custom_external_type_placeholder": "WeaveInterface",
- "custom_external_json_label": "Additional options (JSON object)",
- "custom_external_json_placeholder": "\"listen_ip\": \"0.0.0.0\", \"listen_port\": 4242",
- "custom_external_docs_hint": "See the Interfaces manual for native types and external modules.",
- "custom_external_docs_link": "Open bundled Reticulum manual",
- "custom_external_type_required": "Enter the Reticulum interface type name.",
- "custom_external_json_invalid": "Invalid JSON for extra options.",
- "failed_save_discovery": "Failed to save discovery settings",
- "no_interfaces_found_config": "No interfaces were found in the selected configuration file",
- "failed_parse_config": "Failed to parse configuration file",
- "select_config_file": "Please select a configuration file",
- "select_at_least_one": "Please select at least one interface to import",
- "import_success": "Interfaces imported successfully. MeshChat must be restarted for these changes to take effect.",
- "failed_import_all": "Failed to import interfaces",
- "add_interface_sidebar_a11y": "Shortcuts and community presets",
- "community_quick_start": "Community Quick-Start",
- "community_quick_start_hint": "Presets from directory.rns.recipes online listings.",
- "community_quick_start_hide": "Hide suggested interfaces",
- "community_use_preset": "Use This",
- "community_presets_hidden_hint": "Community presets are hidden. Show them again here or in Settings.",
- "community_presets_show_again": "Show community presets",
- "community_presets_empty": "No presets loaded. Rebuild the bundle or add public/community_interfaces.json.",
- "community_presets_refresh": "Refresh community presets from directory.rns.recipes",
- "community_presets_refreshed": "Updated {count} community interface preset(s)",
- "community_presets_refresh_failed": "Could not refresh community presets",
- "i2p_requirements_title": "I2P interface rules",
- "i2p_requirements_body": "Only one I2P interface is allowed. Enable Transport Mode in Settings first, then add I2P last. Do not import I2P from a config file or edit it in the raw config editor. Changes that leave I2P mid-list are repaired automatically on save and startup.",
- "i2p_transport_required": "Enable Transport Mode in Settings before adding an I2P interface.",
- "i2p_already_exists": "An I2P interface already exists. Remove it before adding another.",
- "i2p_import_forbidden": "I2P interfaces cannot be imported from a file. Add I2P only through Add Interface.",
- "find_more_nodes": "Find more nodes",
- "quick_import": "Quick Import",
- "quick_import_paste_hint": "Paste raw config",
- "quick_import_placeholder": "[Interface Name]\ntype = TCPClientInterface\ntarget_host = 1.2.3.4",
- "quick_import_apply": "Apply: {name}",
- "discovered_network_name": "Network Name",
- "discovered_passphrase": "Passphrase",
- "discovered_copy_network_name": "Copy IFAC network name",
- "discovered_copy_passphrase": "Copy IFAC passphrase",
- "discovered_use_this": "Use this interface",
- "discovered_copy_config": "Copy config block",
- "discovered_config_block": "Config block",
- "discovered_no_config": "This announce did not include a ready-to-paste config block.",
- "discovered_prefill_applied": "Prefilled fields from discovered interface.",
- "rnode_ble_toggle": "Bluetooth LE (RNode UART)",
- "rnode_ble_hint": "Reticulum uses Bluetooth on this device. Pair the RNode in your OS settings first. This requires the Desktop or Android app. On Android, you must also allow the required permissions.",
- "rnode_ble_peer_label": "BLE name or MAC",
- "rnode_ble_peer_placeholder": "MyRNode or aa:bb:cc:dd:ee:ff",
- "rnode_ble_peer_required": "Enter a BLE advertised name or MAC address for the RNode.",
- "rnode_ip_host_label": "Host or IP address",
- "rnode_tcp_port_fixed_hint": "Reticulum uses port 7633 on the RNode. Enter only the hostname or IP without the port.",
- "rnode_tcp_host_required": "Enter the hostname or IP for RNode over TCP.",
- "listen_ip_required": "Listen IP is required.",
- "kernel_iface_picker_title": "Interfaces on this host",
- "kernel_iface_loading": "Loading…",
- "kernel_iface_picker_help": "Device is optional: one kernel interface name, or leave empty to bind using Listen IP only. Click a row to fill the field.",
- "auto_iface_ifname_chips_hint": "This host's interfaces: click to add or remove names in the field above."
- },
- "map": {
- "title": "Map",
- "pop_out": "Pop out map",
- "description": "Offline map tiles (MBTiles) and your own layers.",
- "upload_mbtiles": "Upload MBTiles",
- "select_file": "Select MBTiles file",
- "offline_mode": "Offline Mode",
- "online_mode": "Online Mode",
- "attribution": "Attribution",
- "bounds": "Bounds",
- "center": "Center",
- "zoom": "Zoom",
- "uploading": "Uploading...",
- "upload_success": "Map uploaded successfully",
- "upload_failed": "Failed to upload map",
- "invalid_file": "Invalid MBTiles file. Only raster tiles are supported.",
- "default_view": "Default View",
- "set_as_default": "Set as Default View",
- "export_area": "Export Area",
- "export_instructions": "Drag on the map to select an area.",
- "min_zoom": "Min Zoom",
- "max_zoom": "Max Zoom",
- "tile_count": "Estimated Tiles",
- "start_export": "Start Export",
- "exporting": "Exporting Map...",
- "download_ready": "Export Complete",
- "download_now": "Download MBTiles",
- "caching_enabled": "Tile Caching",
- "clear_cache": "Clear Cache",
- "cache_cleared": "Tile cache cleared",
- "tile_server_url": "Tile Server URL",
- "tile_server_url_placeholder": "https://tiles.openfreemap.org/styles/bright",
- "tile_server_url_hint": "Use {z}, {x}, {y} for zoom, column, row",
- "tile_server_saved": "Tile server URL saved",
- "tile_server_openstreetmap": "OpenStreetMap",
- "nominatim_api_url": "Nominatim API URL",
- "nominatim_api_url_placeholder": "https://nominatim.openstreetmap.org",
- "nominatim_api_url_hint": "Base URL for Nominatim geocoding service",
- "nominatim_api_saved": "Nominatim API URL saved",
- "search_placeholder": "Search for a location...",
- "search_offline_error": "Search is only available in online mode",
- "search_connection_error": "Failed to connect to search service. Please check your internet connection.",
- "search_timeout_error": "The geocoder did not respond in time. It may be slow, overloaded, or unreachable. Try again.",
- "search_error": "Search error",
- "tile_connectivity_title": "Map tiles could not be loaded",
- "tile_connectivity_body": "Check your network, try again, use a local tile server in settings, or switch to offline mode if you have MBTiles loaded.",
- "tile_connectivity_retry": "Retry tiles",
- "tile_connectivity_use_offline": "Use offline mode",
- "tile_connectivity_dismiss": "Dismiss",
- "tile_failover_trying": "Map tiles failed; trying {provider}…",
- "tile_failover_offline": "Online tiles unavailable; switching to offline mode",
- "search_no_results": "No results found",
- "custom_tile_server_unavailable": "Custom tile server is not accessible in offline mode",
- "custom_nominatim_unavailable": "Custom Nominatim server is not accessible in offline mode",
- "onboarding_title": "Export to MBTiles!",
- "onboarding_text": "Use the export tool to download map areas as MBTiles files for offline use.",
- "onboarding_got_it": "Got it",
- "tool_point": "Draw Point",
- "tool_linestring": "Draw Line",
- "tool_polygon": "Draw Polygon",
- "tool_circle": "Draw Circle",
- "tool_measure": "Measure Distance/Area",
- "tool_bearing": "Bearing and distance (two taps, offline)",
- "tool_bearing_from_here": "Bearing from my location to one tap",
- "bearing_geodesic": "Geodesic (great circle)",
- "bearing_rhumb": "Rhumb line (constant bearing)",
- "bearing_forward": "Forward azimuth",
- "bearing_back": "Back azimuth",
- "bearing_rhumb_line": "Rhumb bearing / back",
- "bearing_hint_first": "Tap the start point, then the end point.",
- "bearing_hint_second": "Tap the end point.",
- "bearing_hint_destination": "Tap your destination on the map.",
- "bearing_from_here": "Use my location as start",
- "bearing_help_first": "Tap start, then end",
- "bearing_help_drag": "Move the pointer for a live readout",
- "tool_clear": "Clear Drawings",
- "save_drawing": "Save Drawing Layer",
- "load_drawing": "Load Drawing Layer",
- "save_drawing_title": "Save Drawing Layer",
- "save_drawing_desc": "Save your current map annotations to the database.",
- "drawing_name": "Layer Name",
- "drawing_name_placeholder": "e.g., Target Area Alpha",
- "load_drawing_title": "Load Drawing Layer",
- "loading_drawings": "Loading saved layers...",
- "no_drawings": "No Saved Layers",
- "no_drawings_desc": "You haven't saved any map annotations yet.",
- "saved_on": "Saved on",
- "settings": "Map Settings",
- "go_to_my_location": "Go to My Location",
- "source_updated": "Map source updated",
- "failed_set_active": "Failed to set active map",
- "file_deleted": "File deleted",
- "failed_delete_file": "Failed to delete file",
- "storage_saved": "Storage directory saved",
- "failed_save_storage": "Failed to save directory",
- "export_cancelled": "Export cancelled",
- "failed_cancel_export": "Failed to cancel export",
- "failed_start_export": "Failed to start export",
- "select_mbtiles_error": "Please select an .mbtiles file",
- "view_saved": "Default view saved",
- "failed_save_view": "Failed to save default view",
- "failed_clear_cache": "Failed to clear cache",
- "failed_save_tile_server": "Failed to save tile server URL",
- "failed_save_nominatim": "Failed to save Nominatim API URL",
- "copied_coordinates": "Copied coordinates",
- "failed_load_drawings": "Failed to load drawings",
- "not_initialized": "Map not initialized",
- "drawing_saved": "Drawing saved",
- "failed_save_drawing": "Failed to save drawing",
- "deleted": "Deleted",
- "failed_delete": "Failed to delete",
- "location_not_determined": "Could not determine your location",
- "geolocation_not_supported": "Geolocation is not supported by your browser",
- "no_nodes_location": "No discovered nodes with location found",
- "failed_fetch_nodes": "Failed to fetch discovered nodes for mapping",
- "share_view": "Share map view (copy links)",
- "share_message_prefix": "MeshChatX map:",
- "share_copied": "Map links copied to clipboard",
- "ping_here": "Ping here (send in chat)",
- "ping_here_toolbar": "Ping map center in chat",
- "ping_modal_title": "Send map ping",
- "ping_destination": "Destination",
- "ping_pick_conversation": "Choose a conversation",
- "ping_send": "Send ping",
- "ping_sent": "Map ping sent",
- "ping_failed": "Could not send map ping",
- "ping_invalid_destination": "Pick a valid 32-character destination hash",
- "ping_message_prefix": "MeshChatX map ping:",
- "north_up": "North is up (map not rotated)",
- "export_tile_limit_exceeded": "Too many tiles for one export. Lower max zoom or shrink the area.",
- "export_region_world": "World (low zoom)",
- "export_region_europe": "Europe",
- "export_region_north_america": "North America",
- "export_region_south_america": "South America",
- "export_region_africa": "Africa",
- "export_region_asia": "Asia",
- "export_region_oceania": "Oceania",
- "vector_exchange_title": "GeoJSON / KML / KMZ",
- "vector_exchange_merge": "Merge with existing drawings (uncheck to replace)",
- "vector_exchange_hint": "Imports support icons from various formats including GeoJSON and KML. Select a drawing to see its metadata.",
- "vector_import_geojson": "Import GeoJSON",
- "vector_import_kml": "Import KML",
- "vector_import_kmz": "Import KMZ",
- "vector_export_geojson": "Export GeoJSON",
- "vector_export_kml": "Export KML",
- "vector_export_kmz": "Export KMZ",
- "vector_import_ok": "Imported {count} feature(s).",
- "vector_import_empty": "No features found in file.",
- "vector_import_failed": "Could not read vector file.",
- "vector_export_ok": "Export started.",
- "remote_overlays_title": "Remote overlays",
- "remote_overlays_reload": "Reload",
- "remote_overlays_kind": "Source type",
- "remote_overlays_url": "Source URL",
- "remote_overlays_paths": "Repo file paths (one per line)",
- "remote_overlays_ref": "Git ref (branch, tag, or commit)",
- "remote_overlays_refresh_interval": "Autorefresh interval (seconds, 0 = off)",
- "remote_overlays_import": "Import / fetch",
- "remote_overlays_importing": "Fetching…",
- "remote_overlays_empty": "No remote overlays yet.",
- "remote_overlays_visible": "Show",
- "remote_overlays_refresh": "Refresh",
- "remote_overlays_copy_drawings": "Copy to drawings",
- "remote_overlays_delete": "Delete",
- "remote_overlays_error": "Remote overlay error",
- "remote_overlays_export_ok": "Overlay export started.",
- "remote_overlays_export_failed": "Overlay export failed.",
- "remote_overlays_copied": "Copied overlay into drawings.",
- "drop_geo_files": "Drop map file here",
- "drop_map_files_hint": "GeoJSON, KML, KMZ, or MBTiles",
- "drop_no_geo_files": "No GeoJSON, KML, or KMZ files detected.",
- "drop_no_supported_files": "No GeoJSON, KML, KMZ, or MBTiles files detected.",
- "new_tab": "New map",
- "new_tab_shortcut": "New map (Ctrl+T)",
- "tab_default_name": "Map {number}",
- "tab_rename_hint": "Double-click or double-tap to rename"
- },
- "interface": {
- "disable": "Disable",
- "enable": "Enable",
- "edit_interface": "Edit Interface",
- "export_interface": "Export Interface",
- "delete_interface": "Delete Interface",
- "listen": "Listen",
- "forward": "Forward",
- "port": "Port",
- "frequency": "Frequency",
- "bandwidth": "Bandwidth",
- "txpower": "TX Power",
- "spreading_factor": "SF",
- "coding_rate": "Coding Rate",
- "bitrate": "Bitrate",
- "tx": "TX",
- "rx": "RX",
- "noise": "Noise",
- "clients": "Clients",
- "link_up": "Link up",
- "link_down": "Link down",
- "link_unknown": "Link status unknown"
- },
- "messages": {
- "title": "Messages",
- "conversations": "Conversations",
- "announces": "Announces",
- "search_placeholder": "Search {count} conversations...",
- "unread": "Unread",
- "failed": "Failed",
- "attachments": "Attachments",
- "no_messages_yet": "No messages yet",
- "loading_conversations": "Loading conversations...",
- "no_conversations": "No Conversations",
- "discover_peers": "Discover peers on the Announces tab",
- "no_search_results": "No Results Found",
- "try_another_search": "Try a different search term",
- "no_search_results_conversations": "Your search didn't match any conversations.",
- "search_placeholder_announces": "Search {count} recent announces...",
- "no_peers_discovered": "No Peers Discovered",
- "waiting_for_announce": "Waiting for someone to announce!",
- "no_search_results_peers": "Your search didn't match any Peers!",
- "searching_announces": "Searching announces...",
- "direct": "Direct",
- "downloading": "Downloading",
- "hops": "{count} hops",
- "hops_away": "{count} hops away",
- "snr": "SNR {snr}",
- "stamp_cost": "Stamp Cost {cost}",
- "stamp_ticket_valid": "Outbound stamp ticket active (instant send). Expires {expires}.",
- "stamp_ticket_expired": "Stamp ticket expired; the next message may require proof-of-work.",
- "pop_out_chat": "Pop out chat",
- "open_in_split": "Open in split view",
- "select_conversation_for_pane": "Select a conversation to open it here",
- "more_actions": "More actions",
- "retry_failed": "Retry failed messages",
- "telemetry_history": "Telemetry history",
- "telemetry_history_modal_title": "Telemetry History",
- "telemetry_history_empty": "No telemetry history found for this peer.",
- "telemetry_history_done": "Done",
- "telemetry_show_in_chat": "Show telemetry in main chat",
- "telemetry_battery_trend": "Battery trend",
- "telemetry_battery_range": "{from}% → {to}%",
- "telemetry_label_sent": "Sent",
- "telemetry_label_received": "Received",
- "telemetry_battery_level": "Battery: {percent}%",
- "telemetry_snr_db": "SNR: {snr} dB",
- "telemetry_location_request": "Location Request",
- "start_call": "Call",
- "share_contact": "Share contact",
- "share_contact_modal_title": "Share contact",
- "share_contact_search_placeholder": "Search contacts…",
- "share_apk": "Share app (APK)",
- "share_apk_failed": "Could not open the share sheet for the APK.",
- "custom_display_name": "Custom Display Name",
- "stranger_banner_text": "This peer is not in your contacts. Attachments from strangers are blocked.",
- "add_to_contacts": "Add to Contacts",
- "dismiss": "Dismiss",
- "attachments_stripped_notice": "Attachments were blocked from this unknown sender.",
- "stranger_link_open_confirm": "This link was sent by an unknown contact:\n{url}\n\nOpen it anyway?",
- "send_placeholder": "Type a message...",
- "compose_hint_automatic": "Message for automatic delivery",
- "compose_hint_direct": "Message for direct delivery",
- "compose_hint_opportunistic": "Message for opportunistic delivery",
- "compose_hint_propagated": "Message for propagated delivery",
- "send_menu_more_label": "Other Sending Options",
- "send_menu_telemetry_request": "Send as Command or Request",
- "send_menu_paper_compose": "Paper message from composition (LXM)",
- "sending_ellipsis": "Sending…",
- "outbound_pending_propagation": "Sending to propagation node…",
- "outbound_pending_propagation_with_progress": "Sending to propagation node… {progress}%",
- "outbound_solving_stamps": "Solving stamps…",
- "outbound_solving_stamps_short": "Solving stamps",
- "outbound_pathfinding_tooltip": "Finding path: Reticulum is resolving a route to this peer. Your message sends when the route is ready.",
- "outbound_pathfinding_short": "Finding path",
- "path_no_route": "No path",
- "path_no_route_hint": "No route to this peer yet. Use the path finder or send a message to discover one.",
- "path_loading": "Checking path…",
- "path_stale_label": "stale",
- "path_unresponsive_label": "unresponsive",
- "path_stale_hint": "This path entry is expired and may be rediscovered before the next send.",
- "path_unresponsive_hint": "Reticulum marked this path as unresponsive.",
- "path_info_title": "Path information",
- "path_already_available": "Path is already available",
- "outbound_preparing_message": "Preparing message…",
- "outbound_preparing_message_short": "Preparing message",
- "outbound_sending_with_progress": "Sending… {progress}%",
- "transfer_progress_hop_one": "1 hop",
- "transfer_progress_hops": "{count} hops",
- "outbound_sending_short": "Sending",
- "outbound_pending": "Pending",
- "outbound_outbound_short": "Outbound",
- "send_pathfinding_tooltip": "Finding path to peer (Reticulum is resolving a route). Your message sends as soon as the route is ready. Hover status icons in the bubble for details.",
- "outbound_on_propagation_node": "On propagation node",
- "outbound_sent_network": "Sent",
- "outbound_delivered": "Delivered",
- "outbound_delivered_propagated": "Delivered via propagation",
- "no_messages_in_conversation": "No messages in this conversation yet.",
- "say_hello": "Say hello!",
- "no_active_chat": "No Active Chat",
- "mobile_compose_title": "New message",
- "mobile_compose_destination_placeholder": "32-character destination hash…",
- "select_peer_or_enter_address": "Select a peer from the sidebar or enter an address below",
- "add_files": "Add Files",
- "paste_from_clipboard": "Paste from Clipboard",
- "location": "Location",
- "share_location": "Share Location",
- "request_location": "Request Location",
- "view_on_map": "View on Map",
- "request": "Request",
- "generate_paper_message": "Generate Paper Message (LXM)",
- "ingest_paper_message": "Ingest Paper Message",
- "scan_qr": "Scan QR Code",
- "scanner_hint": "Point your camera at an lxmf://, lxma://, or lxm:// QR code.",
- "camera_not_supported": "Camera QR scanning is not supported in this browser.",
- "camera_permission_denied": "Camera permission denied. Please allow camera access.",
- "camera_not_found": "No camera device found.",
- "camera_failed": "Failed to start camera scanner.",
- "invalid_qr_uri": "Invalid QR content. Expected an lxmf://, lxma://, or lxm:// URI.",
- "recording": "Recording: {duration}",
- "nomad_network_node": "Nomad Network Node",
- "toggle_source": "Toggle Source Code",
- "folder_created": "Folder created",
- "failed_create_folder": "Failed to create folder",
- "folder_renamed": "Folder renamed",
- "failed_rename_folder": "Failed to rename folder",
- "folder_deleted": "Folder deleted",
- "failed_delete_folder": "Failed to delete folder",
- "moved_to_folder": "Moved to folder",
- "failed_move_folder": "Failed to move to folder",
- "mark_as_read": "Mark as Read",
- "mark_all_as_read": "Mark all as read",
- "marked_read": "Marked as read",
- "marked_all_read": "All conversations marked as read",
- "failed_mark_read": "Failed to mark as read",
- "conversations_deleted": "Conversations deleted",
- "failed_delete_conversations": "Failed to delete conversations",
- "failed_export_folders": "Failed to export folders",
- "folders_imported": "Folders imported",
- "failed_import_folders": "Failed to import folders",
- "failed_read_clipboard": "Failed to read from clipboard",
- "clipboard_read_requires_secure_context": "Clipboard read needs a secure page (https or http://127.0.0.1). Using http://0.0.0.0 or another non-local HTTP origin blocks the Clipboard API—open the app on 127.0.0.1 or https, or paste with Ctrl+V.",
- "clipboard_write_unavailable": "Could not copy automatically. Open on https or http://127.0.0.1, or select the text and copy manually.",
- "oversized_body_notice": "This message is too large to display here ({count} characters). Copy the full text to read or edit it elsewhere.",
- "oversized_body_copy": "Copy full text",
- "oversized_body_copied": "Full message text copied",
- "date_divider_today": "Today",
- "date_divider_yesterday": "Yesterday",
- "raw_path_interface_at_send": "Path interface (when recorded)",
- "raw_path_hops_at_send": "Path hops (when recorded)",
- "raw_path_value_unknown": "Unknown",
- "raw_path_finding_measure": "Pathfinding at send (outbound)",
- "raw_path_row_hash_rnpath": "Path table hash (rnpath)",
- "raw_path_row_hash_rnpath_hint": "The destination hash used by Reticulum for this route. You can find it in the path table.",
- "copy_message": "Copy message",
- "message_copied": "Message copied",
- "failed_copy_message": "Could not copy message",
- "failed_send_ingest": "Failed to send ingest request",
- "address_copied": "Your LXMF address copied to clipboard",
- "map_link_share_title": "Shared map view",
- "relay_link_hub_title": "Shared Relay Chat hub",
- "relay_link_room_title": "Shared Relay Chat room",
- "relay_link_join": "Join in Relay Chat",
- "relay_link_copy_uri": "Copy relay link",
- "relay_link_copied": "Relay link copied",
- "relay_link_opened": "Opened in Relay Chat",
- "relay_link_invalid": "Invalid relay link",
- "relay_link_failed": "Could not open relay link",
- "relay_link_disabled": "Relay Chat is disabled in settings",
- "unknown_meshchatx_link": "Unknown meshchatx link ({host})",
- "unknown_meshchatx_link_generic": "Unknown or invalid meshchatx link",
- "map_link_ping_title": "Map ping",
- "map_link_open": "Open on map",
- "map_link_copy_uri": "Copy meshchatx link",
- "map_link_copied": "Map link copied",
- "hash_copied": "Identity hash copied to clipboard",
- "failed_to_copy_hash": "Failed to copy identity hash",
- "copy_hash": "Copy Hash",
- "failed_copy_address": "Failed to copy address",
- "translation_failed": "Translation failed",
- "translate_select_target": "Translate to",
- "translating_message": "Translating...",
- "translated_from_to": "Translated from {source} to {target}",
- "show_original": "Show original",
- "show_translation": "Show translation",
- "translate_message": "Translate message",
- "translate_target_invalid": "Choose a different target language than the source",
- "failed_add_contact": "Failed to add contact",
- "ingesting_paper_message": "Ingesting paper message...",
- "failed_ingest_paper": "Failed to ingest paper message",
- "failed_to_send": "Failed to send message",
- "failed_to_send_image": "Failed to send image {index}: {detail}",
- "enter_display_name": "Enter a custom display name",
- "failed_update_display_name": "Failed to update display name",
- "failed_load_audio": "Failed to load audio attachment.",
- "no_contacts_telephone": "No contacts found in telephone",
- "failed_load_contacts": "Failed to load contacts",
- "location_request_sent": "Location request sent",
- "failed_send_location_request": "Failed to send location request",
- "fetching_location": "Fetching location...",
- "location_disabled": "Location sharing is disabled. Enable it in Settings > Location.",
- "location_sent": "Location shared successfully",
- "location_requested": "Location requested",
- "remove_image_confirm": "Are you sure you want to remove this image attachment?",
- "failed_start_recording": "failed to start recording",
- "failed_start_recording_help_mediadevices": "This web view does not expose a microphone API.",
- "failed_start_recording_help_web_audio": "Web Audio is not available in this browser.",
- "failed_start_recording_help_audio_worklet": "AudioWorklet is not supported — OPUS voice notes require it.",
- "failed_start_recording_help_permission": "Allow microphone access, or try again after another app releases the microphone.",
- "remove_audio_confirm": "Are you sure you want to remove this audio attachment?",
- "failed_generate_qr": "Failed to generate QR",
- "enter_folder_name": "Enter folder name",
- "new_folder": "New Folder",
- "rename_folder": "Rename folder",
- "failed_render_qr": "Failed to render QR code",
- "uri_copied": "URI copied to clipboard",
- "failed_copy_uri": "Failed to copy URI",
- "paper_message_sent": "Paper message sent successfully",
- "failed_send_paper": "Failed to send paper message",
- "failed_load_config": "Failed to load configuration",
- "profile_icon_saved": "Profile icon saved successfully",
- "failed_save_profile_icon": "Failed to save profile icon",
- "select_colors_warning": "Please select both background and icon colors",
- "select_icon_warning": "Please select an icon",
- "changes_reset": "Changes reset to saved values",
- "profile_icon_removed": "Profile icon removed successfully",
- "user_banished": "User banished successfully",
- "failed_banish_user": "Failed to banish user",
- "banish_user": "Banish user",
- "banish_confirm": "Are you sure you want to banish this user? They will not be able to send you messages or establish links.",
- "delete_conversation_confirm": "Are you sure you want to delete this conversation?",
- "failed_delete_conversation": "failed to delete conversation",
- "delete_history_confirm": "Are you sure you want to delete all messages in this conversation? This can not be undone!",
- "failed_delete_history": "failed to delete history",
- "pin_conversation": "Pin conversation",
- "unpin_conversation": "Unpin conversation",
- "failed_toggle_pin": "Could not update pin",
- "opportunistic_deferred_label": "Waiting",
- "opportunistic_deferred_tooltip": "Message will send as soon as the user comes online or announces.",
- "failed_waiting_announce": "Failed, waiting for announce",
- "failed_waiting_announce_tooltip": "Delivery failed. Will resend automatically when the contact is reachable.",
- "invalid_destination_hash": "Invalid destination hash",
- "invalid_destination_hash_format": "Invalid destination hash format",
- "ping_failed": "Ping failed. Try again later",
- "ping_in_progress": "Pinging destination",
- "ping_reply_from": "Valid reply from {hash}",
- "duration": "Duration: {duration}",
- "hops_there": "Hops There: {count}",
- "hops_back": "Hops Back: {count}",
- "signal_quality": "Signal Quality: {quality}%",
- "rssi_val": "RSSI: {rssi}dBm",
- "snr_val": "SNR: {snr}dB",
- "reply": "Reply",
- "replying_to": "Replying to",
- "forward_banner_label": "Forwarded message",
- "forward_you_shared": "You're forwarding this quote",
- "forward_pick_recipient_title": "Forward to contact",
- "forward_message": "Forward",
- "forward_sent": "Message forwarded",
- "forward_failed": "Could not forward message",
- "forward_no_lxmf_destination": "This contact has no usable LXMF delivery address.",
- "forward_verify_show": "Source and verification",
- "forward_verify_hide": "Hide verification",
- "forward_verify_explanation": "Paper LXMF links only work if the full message is available and fits Paper limits. Quoted snippets can be faked.",
- "forward_verify_loading": "Looking up verification URI…",
- "forward_verify_open_uri": "Open verification URI",
- "forward_verify_unavailable": "Original message bytes are not cached here, so no verification URI could be built.",
- "forward_verify_not_cached": "Original message bytes are not cached here.",
- "forward_verify_failed": "Verification lookup failed.",
- "forward_content_stub": "_Forwarded via MeshChatX_\nAttachments and quoted text belong to the original sender.",
- "forward_open_party_is_self": "That is your own address.",
- "message_actions": "Message actions",
- "save_image_to_device": "Save image to device",
- "cancel_send": "Cancel send",
- "react": "React",
- "reaction_you": "You",
- "reaction_send_failed": "Could not send reaction",
- "conversation_reaction_preview": "{name} reacted {emoji}",
- "conversation_location_share_you": "You shared your location",
- "conversation_location_share_other": "{name} shared their location",
- "conversation_telemetry_preview": "{name} sent telemetry",
- "conversation_telemetry_stream_preview": "{name} sent a telemetry stream",
- "conversation_location_request_in_preview": "{name} requested your location",
- "conversation_location_request_out_preview": "{name} sent a location request",
- "conversation_image_you": "You sent an image",
- "conversation_image_other": "{name} sent an image",
- "conversation_voice_you": "You sent a voice note",
- "conversation_voice_other": "{name} sent a voice note",
- "conversation_file_you": "You sent a file",
- "conversation_file_other": "{name} sent a file",
- "conversation_files_you": "You sent {count} files",
- "conversation_files_other": "{name} sent {count} files",
- "message_not_found_in_cache": "Message not found in cache"
- },
- "settings": {
- "tabs": {
- "general": "General",
- "general_desc": "Language, appearance, battery saver, platform, maps, and shortcuts",
- "messages": "Messages",
- "messages_desc": "LXMF, stickers, and safety",
- "network": "Network",
- "network_desc": "Transport, interfaces, discovery, calls, and security",
- "nomad": "NomadNet",
- "nomad_desc": "Archiver and page rendering",
- "privacy": "Privacy",
- "privacy_desc": "Data, access, security, and banished users",
- "maintenance": "Maintenance",
- "maintenance_desc": "Cleanup, export, import, and sources",
- "plugins": "Plugins",
- "plugins_desc": "Install and manage MeshChatX plugins"
- },
- "shortcut_saved": "Shortcut saved",
- "shortcut_deleted": "Shortcut deleted",
- "keyboard_shortcuts_title": "Keyboard Shortcuts",
- "keyboard_shortcuts_description": "Customize quick keyboard actions. Collapsed by default on phones.",
- "archived_pages_flushed": "Archived pages flushed.",
- "failed_enable_transport": "Failed to enable transport mode!",
- "failed_disable_transport": "Failed to disable transport mode!",
- "failed_update_reticulum_instance": "Failed to update Reticulum instance settings!",
- "failed_reload_reticulum": "Failed to reload Reticulum!",
- "folders_exported": "Folders exported",
- "failed_export_folders": "Failed to export folders",
- "folders_imported": "Folders and mappings imported successfully",
- "failed_import_folders": "Failed to import folders",
- "share_apk_heading": "Share this install",
- "share_apk_desc": "Send the installed MeshChatX package over Bluetooth, Nearby Share, or another app. Only available in the Android app.",
- "share_apk_short_hint": "Opens the system share sheet for this APK.",
- "share_apk": "Share app (APK)",
- "share_apk_failed": "Could not open the share sheet for the APK.",
- "android_privacy_heading": "Device privacy",
- "android_privacy_desc": "Optional Android shell protections. All options are off by default so normal screenshots and clipboard use keep working.",
- "android_block_screenshots": "Block screenshots and recent-app previews",
- "android_block_screenshots_desc": "Uses the Android secure-window flag so screenshots and the app switcher preview cannot capture MeshChatX content.",
- "android_block_screenshots_on": "Screenshot blocking enabled",
- "android_block_screenshots_off": "Screenshot blocking disabled",
- "android_clear_clipboard_on_background": "Clear clipboard when leaving the app",
- "android_clear_clipboard_on_background_desc": "Clears the system clipboard when MeshChatX goes to the background so copied message text is harder to leak to other apps.",
- "android_clear_clipboard_on_background_on": "Clipboard will clear when the app backgrounds",
- "android_clear_clipboard_on_background_off": "Clipboard will no longer clear on background",
- "android_privacy_save_failed": "Could not update Android privacy setting.",
- "battery": {
- "eyebrow": "Power",
- "title": "Battery saver",
- "description": "Reduce UI polling and visualiser work on battery. Messaging and mesh delivery stay active. Each option below applies only while battery saver is on.",
- "enabled": "Enable battery saver",
- "enabled_desc": "Master switch for the power-saving options below.",
- "options_heading": "When battery saver is on",
- "disable_visualiser_discovery": "Hide discovered interfaces in visualiser",
- "disable_visualiser_discovery_desc": "Stops drawing discovered-interface nodes on the network graph.",
- "hide_offline_interfaces": "Hide offline interfaces in visualiser",
- "hide_offline_interfaces_desc": "Only show online (and path-table) interfaces on the graph.",
- "max_visualiser_interfaces": "Max interfaces on visualiser",
- "max_visualiser_interfaces_desc": "0 means unlimited. Prefer online interfaces when truncating.",
- "visualiser_reload_seconds": "Visualiser auto-reload interval (seconds)",
- "visualiser_reload_seconds_desc": "0 disables auto-reload while saver is on. Default without saver is 15s.",
- "disable_visualiser_live_layout": "Disable visualiser live layout",
- "disable_visualiser_live_layout_desc": "Turns off continuous layout/physics work on the graph.",
- "reduce_background_polling": "Slow background status polling",
- "reduce_background_polling_desc": "Multiplies App shell poll intervals (calls, unread, app info).",
- "background_poll_multiplier": "Background poll multiplier",
- "background_poll_multiplier_desc": "2–10. Applied to App shell intervals when the option above is on.",
- "reduce_interfaces_discovery": "Slow Interfaces page polls",
- "reduce_interfaces_discovery_desc": "Uses the intervals below for stats and discovered-interface refresh.",
- "interfaces_stats_poll_seconds": "Interfaces stats poll (seconds)",
- "interfaces_discovery_poll_seconds": "Discovered interfaces poll (seconds)",
- "apply_interface_bitrate_limits": "Force interface bitrates",
- "apply_interface_bitrate_limits_desc": "Write forced bitrate (bps) into selected Reticulum interfaces and reload RNS when applied.",
- "interface_bitrate_limits_help": "Enter a bitrate in bits per second for each interface you want to cap. Leave blank to skip. Apply writes the config and reloads Reticulum.",
- "interface_bitrate_limits_empty": "No interfaces found yet.",
- "interface_bitrate_placeholder": "bps",
- "apply_bitrates_reload": "Apply bitrates and reload RNS",
- "restore_bitrates_reload": "Restore previous bitrates and reload",
- "bitrates_applied": "Updated {count} interface bitrate(s) and reloaded RNS",
- "bitrates_restored": "Restored {count} interface bitrate(s) and reloaded RNS",
- "bitrates_none_applied": "No bitrate limits matched current interfaces",
- "bitrates_none_restored": "No previous bitrates to restore",
- "bitrates_apply_failed": "Failed to apply interface bitrates",
- "bitrates_restore_failed": "Failed to restore interface bitrates"
- },
- "nomad_micron_wasm_title": "Micron (WASM)",
- "nomad_micron_wasm_desc_before_link": "Use a faster engine (",
- "nomad_micron_wasm_link_label": "micron-parser-go",
- "nomad_micron_wasm_desc_after_link": "). When on, the WebAssembly build can load and be updated in settings. When off, Micron uses JavaScript only. Pick the default renderer below. You can still switch from the Nomad browser toolbar.",
- "nomad_micron_default_engine_title": "Default Micron renderer (.mu)",
- "nomad_micron_default_engine_desc": "JavaScript is the default. WebAssembly uses micron-parser-go when the engine above is enabled and the module has loaded.",
- "nomad_micron_default_engine_option_js": "JavaScript (micron-parser)",
- "nomad_micron_default_engine_option_wasm": "WebAssembly (micron-parser-go)",
- "micron_wasm_update_open_btn": "Update WASM engine…",
- "micron_wasm_update_modal_title": "Micron WASM engine",
- "micron_wasm_update_active_label": "Active override",
- "micron_wasm_update_active_source": "Source: {source}",
- "micron_wasm_update_source_github": "GitHub release (SHA-256 verified)",
- "micron_wasm_update_source_upload": "Local file (not verified)",
- "micron_wasm_update_active_size": "Size: {bytes} bytes",
- "micron_wasm_update_bundled_only": "Using the WASM bundled with this app. Nothing is fetched from the network until you use the actions below.",
- "micron_wasm_update_isolation_note": "The browser cannot fully isolate WebAssembly from JavaScript. This app only exposes Micron conversion through a narrow API; rendered HTML is still sanitized. wasm_exec.js always comes from the app bundle to match the Go runtime.",
- "micron_wasm_update_github_heading": "Install from GitHub release",
- "micron_wasm_update_github_url_hint": "Press Fetch so the MeshChat server downloads from GitHub (browser stays same-origin for CSP). Source path:",
- "micron_wasm_update_fetch_github": "Fetch and install",
- "micron_wasm_update_upload_heading": "Install from file",
- "micron_wasm_update_upload_warning": "Uploaded WASM is not authenticity-checked. Only use files you trust.",
- "micron_wasm_update_revert_bundled": "Remove override (use bundled WASM)",
- "micron_wasm_update_err_empty_tag": "Enter a release tag (for example v1.0.6).",
- "micron_wasm_update_err_not_wasm": "Choose a file whose name ends with .wasm.",
- "micron_wasm_update_err_too_large": "That file is too large to store as a runtime override.",
- "micron_wasm_update_err_too_small": "That file is too small to be a valid WASM module.",
- "micron_wasm_update_err_activate_failed": "Installed file could not be loaded (check wasm_exec compatibility or try another build). Override was removed.",
- "micron_wasm_update_toast_installed": "Installed Micron WASM {tag}.",
- "micron_wasm_update_toast_uploaded": "Installed WASM from your file.",
- "micron_wasm_update_toast_reverted": "Restored bundled Micron WASM."
- },
- "debug": {
- "title": "Debug Logs",
- "description": "Read application and access logs, filter by severity, and search when troubleshooting.",
- "tab_logs": "Logs",
- "tab_access_attempts": "Access attempts",
- "search_logs_placeholder": "Search logs...",
- "search_access_placeholder": "Search IP, path, outcome...",
- "level_all": "All levels",
- "anomalies_only": "Anomalies only",
- "outcome_all": "All outcomes",
- "loading_logs": "Loading logs...",
- "no_logs": "No logs found.",
- "loading_access": "Loading access attempts...",
- "no_access": "No access attempts recorded.",
- "failed_fetch_logs": "Failed to fetch logs",
- "failed_fetch_access": "Failed to fetch access attempts",
- "logs_copied": "Logs on this page copied to clipboard",
- "access_copied": "Access attempts copied to clipboard",
- "failed_copy_logs": "Failed to copy logs",
- "failed_copy_access": "Failed to copy access attempts"
- },
- "visualiser": {
- "title": "Network Visualiser",
- "description": "Graph of discovered nodes and links.",
- "renderer_title": "Renderer",
- "renderer_desc": "Auto prefers WebGL with WASM when available, then falls back to vis-network. Force a path for debugging or older GPUs.",
- "renderer_option_auto_short": "Auto",
- "renderer_option_webgl_short": "WebGL",
- "renderer_option_vis_short": "Canvas",
- "renderer_option_auto": "Auto (WebGL when available)",
- "renderer_option_webgl": "WebGL + WASM",
- "renderer_option_vis": "vis-network (canvas)",
- "renderer_webgl_unavailable": "WebGL renderer unavailable. Using vis-network fallback.",
- "reticulum_mesh": "Reticulum Mesh",
- "network_visualizer": "Network Visualizer",
- "node_info": "Node Info",
- "hash": "Hash",
- "hops": "Hops",
- "interface": "Interface",
- "rssi": "RSSI",
- "snr": "SNR",
- "first_seen": "First seen",
- "last_seen": "Last seen",
- "center_node": "Center on node",
- "copy_hash": "Copy Hash",
- "banish_identity": "Banish identity",
- "banished_identities": "Banished identities",
- "filter_nodes": "Filter nodes...",
- "all": "All",
- "direct": "Direct",
- "one_hop": "1 Hop",
- "multi_hops": "2+ Hops",
- "clear": "Clear",
- "show_labels": "Show all labels",
- "show_edges": "Show edges",
- "show_tooltips": "Show tooltips",
- "show_rssi": "Show RSSI",
- "show_snr": "Show SNR",
- "show_hops": "Show Hops",
- "physics": "Physics",
- "gravity": "Gravity",
- "spring_length": "Spring length",
- "spring_strength": "Spring strength",
- "damping": "Damping",
- "central_gravity": "Central gravity",
- "edge_strength": "Edge strength",
- "force_atlas_2": "Force Atlas 2",
- "barns_hut": "Barns Hut",
- "repulsion": "Repulsion",
- "reset_physics": "Reset physics",
- "reset_camera": "Reset Camera",
- "clear_graph": "Clear graph",
- "total_nodes": "Total nodes",
- "total_edges": "Total edges",
- "discovered": "Discovered",
- "wait_between_batches": "Wait between batches",
- "batch": "Batch",
- "loading_status": "Loading status",
- "failed_load": "Failed to load network data",
- "max_hops_filter": "Max hops",
- "show_disabled_interfaces": "Show offline interfaces",
- "show_discovered_interfaces": "Show discovered interfaces",
- "refresh": "Refresh",
- "engine": "Engine",
- "engine_webgl": "WebGL",
- "engine_wasm": "WASM",
- "engine_fallback": "JS fallback",
- "engine_checking": "Checking",
- "engine_webgl_hint": "Graph, layout, and draw buffers run in Go WebAssembly. Pixels are drawn with WebGL2",
- "engine_wasm_hint": "Graph build and force layout run in Go WebAssembly. Canvas draw stays in the browser",
- "engine_fallback_hint": "WASM unavailable. Graph build uses JavaScript. Live layout uses vis-network physics",
- "engine_checking_hint": "Detecting WebAssembly support",
- "fps": "FPS",
- "auto_update": "Auto Update",
- "live_layout": "Live Layout",
- "nodes": "Nodes",
- "links": "Links",
- "interfaces": "Interfaces",
- "online": "Online",
- "offline": "Offline",
- "search_nodes_placeholder": "Search nodes ({count})...",
- "clear_search": "Clear search"
- },
- "banishment": {
- "title": "Banished",
- "description": "Review and lift blocks on identities and nodes.",
- "failed_load_banished": "Failed to load banished destinations",
- "search_placeholder": "Search by hash or display name...",
- "loading_items": "Loading banished items...",
- "no_items": "No banished items found",
- "lift_banishment": "Lift Banishment",
- "lift_banishment_confirm": "Are you sure you want to lift the banishment for {name}?",
- "banished_at": "Banished at",
- "banished_until": "Banished until",
- "reason": "Reason",
- "type": "Type",
- "user": "User",
- "node": "Node",
- "blocked_destinations": "Blocked destinations",
- "banishment_lifted": "Banishment lifted successfully",
- "failed_lift_banishment": "Failed to lift banishment",
- "lift_selected": "Lift banishment ({count})",
- "lift_selected_confirm": "Lift banishment for {count} selected items?",
- "lift_selected_success": "Lifted banishment for {count} items",
- "filter_all_types": "All types",
- "filter_rns": "RNS blackhole",
- "sort_newest": "Newest first",
- "sort_oldest": "Oldest first",
- "sort_name": "Name A-Z",
- "result_count": "{count} items"
- },
- "nomadnet": {
- "remove_favourite": "Remove Favourite",
- "add_favourite": "Add Favourite",
- "identify": "Identify",
- "pop_out_browser": "Pop out browser",
- "hide_source": "Hide source",
- "new_tab": "New tab",
- "new_tab_shortcut": "New tab (Ctrl+T)",
- "page_archives": "Page Archives",
- "archive_current_version": "Archive Current Version",
- "no_archives_for_this_page": "No archives for this page",
- "viewing_archived_version_from": "Viewing archived version from {time}",
- "viewing_archived_version": "Viewing archived version",
- "load_live": "Load Live",
- "failed_to_load_page": "Failed to load page",
- "archived_version_available": "An archived version of this page is available.",
- "view_archive": "View Archive",
- "no_active_node": "No Active Node",
- "select_node_to_browse": "Select a Node to start browsing!",
- "open_nomadnet_url": "Open a Nomadnet URL",
- "unknown_node": "Unknown Node",
- "existing_download_in_progress": "An existing download is in progress. Please wait for it to finish before starting another download.",
- "favourites": "Favourites",
- "announces": "Announces",
- "search_favourites_placeholder": "Search {count} favourites...",
- "search_placeholder_announces": "Search {count} recent announces...",
- "rename": "Rename",
- "remove": "Remove",
- "no_favourites": "No favourites",
- "add_nodes_from_announces": "Add nodes from the announces tab.",
- "favourites_search_no_results": "No favourites match your search",
- "favourites_search_try_other": "Try a different search term.",
- "no_favourites_in_section": "No favourites in this section.",
- "search_announces": "Search announces",
- "searching_announces": "Searching announces...",
- "announced_time_ago": "Announced {time} ago",
- "block_node": "Banish Node",
- "no_announces_yet": "No announces yet",
- "no_search_results_peers": "No peers found matching your search.",
- "listening_for_peers": "Listening for peers on the mesh.",
- "block_node_confirm": "Are you sure you want to banish {name}? Their announces will be ignored and they won't appear in the announce stream.",
- "node_blocked_successfully": "Node banished successfully",
- "failed_to_block_node": "Failed to banish node",
- "failed_rename_favourite": "Failed to rename favourite",
- "rename_favourite": "Rename this favourite",
- "remove_favourite_confirm": "Are you sure you want to remove this favourite?",
- "enter_nomadnet_url": "Enter a Nomadnet URL",
- "archiving_page": "Archiving page...",
- "page_archived_successfully": "Page archived successfully.",
- "identify_confirm": "Are you sure you want to identify yourself to this NomadNetwork Node? The page will reload after your identity has been sent.",
- "banishment_lifted": "Banishment lifted successfully",
- "failed_lift_banishment": "Failed to lift banishment",
- "lift_banishment": "Lift Banishment",
- "enter_section_name": "Enter section name",
- "new_section": "New Section",
- "rename_section": "Rename section",
- "delete_section_confirm": "Delete this section? Favourites will move to the main section.",
- "unsupported_url": "unsupported url: ",
- "path_away_suffix": "away",
- "renderer_chip_micron_js": "Micron JS",
- "renderer_chip_micron_wasm": "Micron WASM",
- "renderer_hint_micron_js": "micron-parser (JavaScript)",
- "renderer_hint_micron_wasm": "micron-parser-go (WebAssembly)",
- "renderer_hint_micron_wasm_pending": "Faster version loading or unavailable. Using the standard version.",
- "renderer_popover_micron_wasm_powered": "Powered by ",
- "renderer_popover_micron_wasm_active_tail": " {version}. WebAssembly renderer.",
- "renderer_popover_micron_wasm_pending_tail": " {version} is loading or unavailable. Using the standard version.",
- "renderer_switch_title": "Parser",
- "renderer_menu_js": "Micron: JavaScript",
- "renderer_menu_wasm": "Micron: WebAssembly",
- "renderer_setting_failed": "Could not update Micron renderer",
- "multiline_hint": "Press Enter again to expand to multi-line",
- "renderer_chip_markdown": "Markdown",
- "renderer_chip_html": "HTML",
- "renderer_chip_plaintext": "Text",
- "renderer_hint_markdown": "Markdown renderer",
- "renderer_hint_html": "Sanitized HTML",
- "renderer_hint_plaintext": "Escaped plain text",
- "load_phase_default": "Loading page",
- "load_phase_finding_path": "Looking for a network path to the node",
- "load_phase_establishing_link": "Establishing link to the node",
- "page_download_cancelled": "Download cancelled.",
- "load_phase_requesting_page": "Sending page request",
- "load_phase_transferring": "Receiving page",
- "path_finder": "Path Finder",
- "path_finder_quick_request": "Request path",
- "path_finder_force_find": "Force find path (wait)",
- "path_finder_drop_and_request": "Drop path and re-discover",
- "path_finder_load_archive": "Load latest archive snapshot",
- "path_finder_request_sent": "Path request sent",
- "path_finder_found": "Path found, retrying page",
- "path_finder_not_found": "No path found within timeout",
- "path_finder_dropped_and_requested": "Path dropped, requesting again",
- "path_finder_failed": "Path finder request failed",
- "path_finder_archive_loading": "Looking for archive snapshot...",
- "export_section_favourites": "Export section favourites",
- "section_favourites_exported": "Section favourites exported",
- "section_favourites_export_failed": "Could not export section favourites",
- "sidebar_selection_mode": "Selection mode",
- "bulk_selected_count": "{count} selected",
- "bulk_remove": "Remove",
- "bulk_move_to_section": "Move to section",
- "bulk_remove_favourites_confirm": "Remove {count} favourites from this device?",
- "bulk_remove_favourites_done": "Removed {count} favourites",
- "bulk_block_confirm": "Banish {count} nodes from the announce list?",
- "bulk_block_done": "Banished {count} nodes",
- "bulk_add_to_favourites": "Add to favourites",
- "bulk_add_favourites_done": "Added {count} nodes to favourites",
- "bulk_block_nodes": "Banish",
- "bulk_nothing_to_add_favourites": "Nothing to add (already favourites or none selected)",
- "context_tabs": "Tabs",
- "close_tabs_to_right": "Close tabs to the right",
- "close_other_tabs": "Close other tabs",
- "close_all_tabs": "Close all tabs",
- "download_page": "Download page",
- "download_page_started": "Page download started",
- "download_page_unavailable": "No page content available to download",
- "download_page_failed": "Could not download page",
- "view_source_unavailable": "No page loaded to view source",
- "context_menu_page_unavailable": "No active page for this tab",
- "context_menu_action_failed": "Context menu action failed",
- "context_menu_favourite_failed": "Could not update favourite",
- "favourite_added": "Added to favourites",
- "favourite_removed": "Removed from favourites",
- "tab_switch_failed": "Could not switch to that tab",
- "tab_content_mismatch": "This tab was out of sync and is being reloaded",
- "tab_restore_failed": "Could not restore this tab's page",
- "open_node_failed": "Could not open the NomadNet node"
- },
- "forwarder": {
- "title": "LXMF Forwarder",
- "description": "Forward LXMF between addresses with reply routing preserved.",
- "add_rule": "Add Forwarding Rule",
- "name": "Name",
- "name_placeholder": "Rule name (e.g. Home Node)...",
- "forward_to_hash": "Forward to Hash",
- "destination_placeholder": "Destination LXMF hash...",
- "source_filter": "Source Filter (Optional)",
- "source_filter_placeholder": "Only forward from this hash...",
- "add_button": "Add Rule",
- "active_rules": "Active Rules",
- "no_rules": "No forwarding rules configured.",
- "active": "Active",
- "disabled": "Disabled",
- "forwarding_to": "Forwarding to: {hash}",
- "source_filter_display": "Source filter: {hash}",
- "delete_confirm": "Are you sure you want to delete this rule?",
- "invalid_hash": "Forward-to hash must be a 32-character hex value",
- "send_failed": "Could not send forwarding update (websocket disconnected)",
- "rule_added": "Forwarding rule added",
- "rule_deleted": "Forwarding rule deleted"
- },
- "archives": {
- "description": "Search Nomad Network pages stored in your local archive.",
- "search_placeholder": "Search content, hash or path...",
- "loading": "Loading archives...",
- "no_archives_found": "No archives found",
- "adjust_filters": "Try adjusting your search filters.",
- "browse_to_archive": "Archived pages will appear here once you browse Nomad Network sites.",
- "page": "Page",
- "pages": "Pages",
- "view": "View",
- "showing_range": "Showing {start} to {end} of {total} archives",
- "page_of": "Page {page} of {total_pages}",
- "export_mu": "Export .mu",
- "export_selected_mu": "Export .mu ({count})",
- "nodes_title": "Nodes",
- "snapshots_title": "Snapshots",
- "select_all": "Select all",
- "delete_selected": "Delete ({count})",
- "viewing_archive": "Viewing archive",
- "select_snapshot": "Select a snapshot to view",
- "open_live": "Open live",
- "search_nodes_placeholder": "Search nodes or content...",
- "no_results": "No results found",
- "no_archives": "No archives yet",
- "show_nodes": "Show nodes",
- "hide_nodes": "Hide nodes",
- "show_snapshots": "Show snapshots",
- "hide_snapshots": "Hide snapshots",
- "delete_snapshot": "Delete snapshot"
- },
- "docs": {
- "title": "Documentation",
- "subtitle": "Reticulum manual and MeshChatX guides, available offline after setup.",
- "tab_meshchatx": "MeshChatX",
- "tab_reticulum": "Reticulum",
- "search_placeholder": "Search documentation...",
- "search_placeholder_mobile": "Search all documentation...",
- "search_results": "Search results",
- "matches_count": "{count} matches",
- "no_results": "No results found",
- "no_results_hint": "Try different keywords or check spelling.",
- "clear_search": "Clear search",
- "versions": "Versions",
- "no_versions": "No versions available",
- "default_version": "Default",
- "upload_zip": "Upload ZIP",
- "open_external": "Open",
- "dismiss": "Dismiss",
- "sections_title": "Guides",
- "on_this_page": "On this page",
- "language_label": "Guide language",
- "select_doc": "Select a guide from the sidebar",
- "no_docs_found": "No MeshChatX guides found",
- "no_docs_hint": "Guides are copied from the docs folder when the app starts.",
- "reticulum_manual": "Reticulum manual",
- "complete_percent": "{percent}% complete",
- "confirm_delete_version": "Delete documentation version \"{version}\"?",
- "prompt_version_name": "Enter a version name for this upload:",
- "status_title": "Documentation Status",
- "status_extracting": "Extracting Documentation...",
- "status_available": "Offline Manual Available",
- "status_not_available": "Manual Not Available",
- "btn_upload": "Upload Manual",
- "btn_share": "Share the Reticulum manual as a re-uploadable ZIP",
- "error": "Error",
- "failed_upload_docs": "Failed to upload documentation",
- "upload_success": "Documentation uploaded",
- "docs_link_copied": "Documentation link copied to clipboard",
- "failed_copy_link": "Failed to copy link",
- "load_list_failed": "Could not load the guide list. Try again in a moment.",
- "load_doc_failed": "Could not load this guide.",
- "search_failed": "Search failed. Check your connection and try again.",
- "manifest_warning": "The documentation index file could not be read. Showing available files without section grouping.",
- "failed_upload_alert": "Failed to upload documentation: {message}",
- "empty_state_hint": "Upload a Reticulum manual ZIP to view it offline. Releases ship a bundled offline copy built at packaging time."
- },
- "licenses": {
- "section_label": "Legal",
- "title": "Third-party licenses",
- "description": "Shows versions and licenses for all software components. This data is collected from the local environment.",
- "short_description": "Dependency versions, authors, and licenses for backend and frontend.",
- "search_placeholder": "Search packages, versions, authors, licenses...",
- "generated_at": "Generated at {time}",
- "frontend_source": "Frontend data: {source}",
- "backend_section": "Backend (Python)",
- "frontend_section": "Frontend (Node)",
- "col_package": "Package",
- "col_version": "Version",
- "col_author": "Author",
- "col_license": "License"
- },
- "tools": {
- "utilities": "Utilities",
- "power_tools": "Utilities",
- "diagnostics_description": "Mesh and Reticulum utilities in one place: reachability, paths, file transfer, translation, documentation, and hardware helpers. Open any row for the full tool.",
- "back_to_tools": "Back to tools",
- "beta_badge": "Beta",
- "alpha_badge": "Alpha",
- "ping": {
- "title": "Ping",
- "description": "Measure round-trip time to another node or app that answers ping."
- },
- "rnprobe": {
- "title": "RNProbe",
- "description": "Send test payloads over the mesh to check reachability and delay."
- },
- "rncp": {
- "title": "RNCP",
- "description": "Send files to or fetch files from another destination over Reticulum, with progress and optional compression."
- },
- "rnsh": {
- "title": "RNSH",
- "description": "Manage persistent rnsh sessions"
- },
- "rnx": {
- "title": "RNX",
- "description": "Remote command execution over Reticulum"
- },
- "rnstatus": {
- "title": "RNStatus",
- "description": "Per-interface traffic counters and link status from your Reticulum instance."
- },
- "rnpath": {
- "title": "RNPath",
- "description": "Inspect paths and announce queues, and request or drop routes when you need to steer traffic.",
- "failed_fetch": "Failed to fetch path data",
- "path_dropped": "Path dropped",
- "failed_drop": "Could not drop path",
- "error_drop": "Error dropping path",
- "failed_request": "Failed to request path",
- "path_requested": "Path requested for {hash}...",
- "drop_confirm": "Are you sure you want to drop the path to {hash}?",
- "drop_via_confirm": "Drop ALL paths via {hash}?",
- "invalid_hops": "Hops filter must be a number",
- "paths_dropped": "Paths dropped",
- "failed_drop_paths": "Failed to drop paths",
- "purge_confirm": "Purge all announce queues? This cannot be undone.",
- "queues_purged": "Announce queues purged",
- "failed_purge": "Failed to purge queues"
- },
- "rnpath_trace": {
- "title": "RNS Path Trace",
- "description": "List each hop along the path to a destination hash.",
- "destination_hash": "Destination Hash",
- "placeholder": "Enter 32-char destination hash...",
- "trace": "Trace Path",
- "tracing": "Tracing path through the mesh...",
- "total_hops": "Total Hops",
- "interface": "Exit Interface",
- "next_hop": "Next Hop",
- "ready_title": "Trace a Path",
- "ready_desc": "Enter a destination hash to list hops along the current path.",
- "unknown_hops": "{count} Intermediate Hops",
- "ping_test": "Test with Ping"
- },
- "translator": {
- "title": "Translator",
- "description": "Translate text with LibreTranslate over the network or Argos Translate on this device."
- },
- "forwarder": {
- "title": "Forwarder",
- "description": "Forward LXMF messages between addresses while keeping reply routing intact."
- },
- "propagation_nodes": {
- "title": "Propagation nodes",
- "description": "Pick preferred mesh propagation nodes, watch live stats, and run quick path checks.",
- "load_failed": "Failed to load propagation nodes",
- "local_restarted": "Local propagation node restarted",
- "local_stopped": "Local propagation node stopped",
- "local_started": "Local propagation node started"
- },
- "sieve_filters": {
- "title": "Sieve filters",
- "description": "Auto-sort mail: match words in peer names or message text, then hide, silence, move to a folder, or block the sender.",
- "subtitle": "Rules run in order. Scope picks everyone, contacts only, or non-contacts only. Choose substring or regex matching. The first enabled rule whose scope fits and whose targets match wins.",
- "back_tools": "Back to tools",
- "rules_heading": "Rules",
- "add_rule": "Add rule",
- "order_hint": "Drag order is top to bottom: the first matching rule applies.",
- "empty_rules": "No rules yet. Add a rule or save an empty list to clear filters.",
- "enabled": "Enabled",
- "move_up": "Move up",
- "move_down": "Move down",
- "remove_rule": "Remove rule",
- "terms_label": "Terms or patterns",
- "terms_placeholder": "One per line or separated by commas. Matches are case-insensitive. You can also use regular expressions (one per line).",
- "match_targets_label": "Match in",
- "match_peer_fields": "Peer display name, custom name, contact fields",
- "match_message": "Message title and body (when available)",
- "match_targets_hint": "At least one target must stay enabled.",
- "match_mode_label": "Match style",
- "match_mode_substring": "Substring",
- "match_mode_regex": "Regular expression",
- "scope_label": "Applies to",
- "scope_everyone": "Everyone",
- "scope_contacts": "Contacts only",
- "scope_non_contacts": "Non-contacts only",
- "graph_scope_everyone": "Everyone",
- "graph_scope_contacts": "Contacts",
- "graph_scope_non_contacts": "Non-contacts",
- "graph_match_peer": "peer",
- "graph_match_message": "msg",
- "graph_match_mode_substring": "substring",
- "graph_match_mode_regex": "regex",
- "action_label": "Then",
- "action_hide": "Hide conversation",
- "action_ignore": "Ignore (no notifications)",
- "action_folder": "Move to folder",
- "action_banish": "Banish sender (full block)",
- "folder_label": "Folder",
- "save": "Save",
- "saving": "Saving…",
- "revert": "Reload",
- "saved": "Sieve filters saved",
- "save_failed": "Could not save sieve filters",
- "load_failed": "Could not load sieve filters",
- "flow_heading": "Rule flow",
- "flow_source": "Peers",
- "flow_source_hint": "Peer fields use announced display name, your custom name, and contact name or LXMF address.",
- "flow_if": "If",
- "flow_hide": "Hide",
- "flow_banish": "Banish",
- "flow_folder": "Folder",
- "flow_no_rules": "No rules"
- },
- "message_blocklist": {
- "title": "Message blocklist",
- "description": "Auto-banish senders when their message or name matches a word or regex from your shared blocklist.",
- "beta_notice_title": "Beta (work in progress)",
- "beta_notice_body": "This tool is off until you turn it on. Words match regardless of case. Regex uses normal patterns. If something matches, the sender gets banished right away. Export a list to share with your group, or import one to merge or replace yours.",
- "enable_label": "Enable message blocklist",
- "enable_hint": "When disabled, entries are saved but no automatic banish runs.",
- "entries_heading": "Blocked phrases",
- "add_entry": "Add phrase",
- "empty_entries": "No phrases yet. Add entries or import a shared list.",
- "entry_enabled": "Active",
- "entry_placeholder": "spam phrase or regex pattern",
- "remove_entry": "Remove",
- "scope_label": "Applies to",
- "scope_everyone": "Everyone",
- "scope_contacts": "Contacts only",
- "scope_non_contacts": "Non-contacts only",
- "match_in_label": "Match in",
- "match_message": "Message title and body",
- "match_peer_fields": "Peer display name and contact fields",
- "match_mode_substring": "Word",
- "match_mode_regex": "Regex",
- "export": "Export",
- "import": "Import",
- "save": "Save",
- "saving": "Saving…",
- "revert": "Reload",
- "saved": "Blocklist saved",
- "save_failed": "Could not save blocklist",
- "load_failed": "Could not load blocklist",
- "enabled_toast": "Message blocklist enabled",
- "disabled_toast": "Message blocklist disabled",
- "exported": "Blocklist exported",
- "export_failed": "Could not export blocklist",
- "import_merge_confirm": "Merge with your current list? Choose Cancel to replace your list entirely.",
- "imported_merge": "Blocklist merged",
- "imported_replace": "Blocklist replaced",
- "import_failed": "Could not import blocklist"
- },
- "repository_server": {
- "title": "Repository server",
- "description": "Redistribute MeshChatX and Reticulum-related Python wheels plus your own files over plain HTTP so people on your network, or on this device, can download them.",
- "subtitle": "Refresh the included software packages, add your own files, or start a download page for other devices.",
- "back_tools": "Back to tools",
- "http_heading": "Plain HTTP mirror",
- "host_label": "Listen address",
- "port_label": "Port",
- "start_http": "Start",
- "stop_http": "Stop",
- "restart_http": "Restart",
- "http_listen_label": "Listening:",
- "open_http": "Open in browser",
- "http_started": "HTTP server started",
- "http_stopped": "HTTP server stopped",
- "http_restarted": "HTTP server restarted",
- "http_err_generic": "HTTP server request failed",
- "http_err_invalid_host": "Invalid listen address",
- "http_err_invalid_port": "Invalid port",
- "http_err_already_running": "HTTP server is already running",
- "http_err_bind_failed": "Could not bind (port in use or permission denied)",
- "upload_heading": "Upload file",
- "choose_file": "Choose file",
- "refresh_bundled_tooltip": "Download bundled wheels to this device (needs network)",
- "refresh_started": "Downloading bundled packages…",
- "refresh_fetching": "Getting {pkg}…",
- "refresh_ok": "Bundled wheels refreshed",
- "refresh_ok_count": "{n} packages downloaded",
- "refresh_failed": "Could not refresh bundled wheels",
- "refresh_pip_missing": "pip was not found on this system. Install pip and try again.",
- "refresh_partial": "Some packages could not be downloaded (details below)",
- "refresh_partial_toast": "Bundled download finished with some failures",
- "files_heading": "Files",
- "col_name": "Name",
- "col_source": "Source",
- "col_size": "Size",
- "empty": "No files yet. Upload a file or refresh bundled wheels.",
- "delete": "Delete",
- "delete_confirm": "Delete uploaded file \"{name}\"?",
- "delete_ok": "File removed",
- "delete_failed": "Could not delete file",
- "upload_ok": "File uploaded",
- "upload_failed": "Upload failed",
- "load_failed": "Could not load repository"
- },
- "rnode_flasher": {
- "title": "RNode Flasher",
- "description": "Flash or update RNode firmware from the browser when your hardware and browser support USB or Wi‑Fi flashing.",
- "select_device": "1. Select your device",
- "product": "Product",
- "model": "Model",
- "connection_method": "Connection Method",
- "serial": "Serial",
- "wifi": "WiFi",
- "ip_address": "IP Address",
- "ip_address_placeholder": "e.g. 192.168.1.100",
- "select_product": "Select a Product",
- "select_model": "Select a Model",
- "enter_dfu_mode": "Enter DFU Mode",
- "entering_dfu_mode": "Entering DFU Mode...",
- "find_device_issue": "Can't find your device? Open an issue on",
- "select_firmware": "2. Select firmware to flash (.zip)",
- "flash_now": "Flash Now",
- "flashing": "Flashing: {percentage}%",
- "flashing_file_progress": "File {current}/{total}: {percentage}%",
- "connecting_device": "Connecting to device...",
- "download_firmware": "Download Firmware",
- "official_firmware": "Official Firmware",
- "ce_firmware": "CE Firmware",
- "transport_node_firmware": "Transport Node Firmware",
- "common_issues": "Common Issues",
- "hardware_failure": "Hardware Failure:",
- "provision_eeprom_hint": "You need to provision the eeprom in step 3.",
- "firmware_corrupt": "Firmware Corrupt:",
- "set_firmware_hash_hint": "You need to set the firmware hash in step 4.",
- "step_provision": "3. Provision EEPROM",
- "provision_description": "Sets device info, checksum and blank signature",
- "provision": "Provision",
- "provisioning_wait": "Provisioning: please wait...",
- "step_set_hash": "4. Set Firmware Hash",
- "set_hash_description": "Uses hash from board",
- "set_firmware_hash": "Set Firmware Hash",
- "setting_hash_wait": "Setting Firmware Hash: please wait...",
- "step_done": "5. Finalize",
- "download_recommended": "Download & Load Recommended Firmware",
- "downloading": "Downloading...",
- "done_description": "If you made it this far, and all previous steps were successful, your RNode should be ready to use.",
- "meshchat_usage": "To use RNode with MeshChat, you will need to add an RNodeInterface in the Interfaces → Add Interface page.",
- "sideband_usage": "To use RNode with Sideband, you will need to configure it in Hardware → RNode and enable Connectivity → Connect via RNode.",
- "restart_warning": "You must restart MeshChat and Sideband for interface setting changes to take effect, otherwise nothing will happen!",
- "advanced_tools": "Advanced Tools",
- "detect_rnode": "Detect RNode",
- "reboot_rnode": "Reboot RNode",
- "read_display": "Read Display",
- "dump_eeprom": "Dump EEPROM",
- "wipe_eeprom": "Wipe EEPROM",
- "eeprom_console_hint": "EEPROM dumps are shown in dev tools console.",
- "configure_bluetooth": "Configure Bluetooth (optional)",
- "bluetooth_info_1": "Bluetooth is not supported on all devices.",
- "bluetooth_info_2": "Some devices use Bluetooth Classic, and some use BLE (Bluetooth Low Energy)",
- "bluetooth_info_3": "Put the RNode into Bluetooth Pairing mode, then connect to it from Android Bluetooth settings.",
- "bluetooth_info_4": "Once you have initiated a pair request from Android, a PIN should show on the RNode display.",
- "bluetooth_info_5": "In Sideband you will need to enable Connect using Bluetooth in Hardware → RNode.",
- "bluetooth_info_6": "If your device uses BLE you will also need to enable Device requires BLE in Hardware → RNode.",
- "bluetooth_restart_warning": "Don't forget to restart Sideband for the setting changes to take effect!",
- "enable": "Enable",
- "disable": "Disable",
- "start_pairing": "Start Pairing",
- "configure_tnc": "Configure TNC Mode (optional)",
- "tnc_info_1": "TNC mode allows an RNode to be used as a KISS compatible TNC over the Serial Port.",
- "tnc_info_2": "This mode makes it usable with amateur radio software that can talk to a KISS TNC over a serial port.",
- "tnc_warning": "You must leave TNC mode disabled when using RNode with apps like MeshChat or Sideband.",
- "frequency": "Frequency (Hz)",
- "bandwidth": "Bandwidth",
- "tx_power": "Tx Power (dBm)",
- "spreading_factor": "Spreading Factor",
- "coding_rate": "Coding Rate",
- "configure_display": "Configure Display",
- "rotation": "Rotation",
- "reconditioning": "Reconditioning",
- "start": "Start",
- "stop": "Stop",
- "rotation_v180_warning": "Setting display rotation requires firmware v1.80+",
- "errors": {
- "failed_download": "Failed to download firmware: {error}",
- "firmware_not_found_in_release": "Recommended firmware not found in the latest release.",
- "web_serial_not_supported": "Web Serial is not supported in this browser. Please use Chrome, Edge, or another Chromium-based browser.",
- "no_device_selected": "No device selected. Please select a serial port.",
- "failed_connect": "Failed to connect to device: {error}",
- "not_an_rnode": "Selected device is not an RNode! Please make sure you've selected the correct serial port.",
- "failed_dfu": "Failed to enter DFU mode: {error}",
- "select_firmware_first": "Please select a firmware file (.zip) first.",
- "failed_flash": "Firmware flashing failed: {error}",
- "failed_ota": "OTA flashing failed: {error}",
- "esptool_not_loaded": "esptool-js could not be loaded. Please refresh the page and try again.",
- "no_flash_config": "Flash configuration is not available for the selected device. Please select a different device or check the firmware file.",
- "failed_extract": "Required file \"{file}\" not found in firmware ZIP file. Please download the correct firmware for your device.",
- "failed_detect": "Failed to detect RNode. Make sure the device is connected and in the correct mode.",
- "provisioned_already": "Eeprom is already provisioned. You must wipe it to reprovision!",
- "select_product_first": "Please select a product!",
- "select_model_first": "Please select a model!",
- "not_provisioned": "Eeprom is not provisioned. You must do this first!",
- "failed_provision": "failed to provision: {error}",
- "failed_set_hash": "failed to set firmware hash: {error}",
- "failed_reboot": "Failed to reboot RNode: {error}",
- "failed_read_display": "Failed to read display: {error}",
- "failed_dump_eeprom": "Failed to dump EEPROM: {error}",
- "failed_wipe_eeprom": "Failed to wipe EEPROM: {error}",
- "failed_enable_tnc": "Failed to enable TNC mode: {error}",
- "failed_disable_tnc": "Failed to disable TNC mode: {error}",
- "failed_enable_bluetooth": "Failed to enable Bluetooth: {error}",
- "failed_disable_bluetooth": "Failed to disable Bluetooth: {error}",
- "failed_start_pairing": "Failed to start pairing: {error}",
- "failed_set_rotation": "Failed to set display rotation: {error}",
- "failed_start_reconditioning": "Failed to start display reconditioning: {error}",
- "no_main_bin": "Could not locate the main firmware binary in the ZIP.",
- "invalid_host": "Please enter a valid host or IP address.",
- "bluetooth_flash_unsupported": "Bluetooth cannot be used to flash firmware. Use Serial or WiFi to flash, then return to Bluetooth for management.",
- "failed_diagnostics": "Failed to run diagnostics: {error}",
- "failed_detect_with_reason": "Failed to detect RNode: {error}"
- },
- "alerts": {
- "firmware_downloaded": "Recommended firmware has been downloaded and loaded.",
- "dfu_ready": "Device should now be in DFU mode. You can now proceed with flashing firmware.",
- "flash_success": "Firmware has been flashed successfully!",
- "rebooting": "Board is rebooting!",
- "eeprom_wipe_confirm": "Are you sure you want to wipe the eeprom on this device? This will take about 30 seconds. An alert will show when the eeprom wipe has finished.",
- "eeprom_wiped": "eeprom has been wiped!",
- "eeprom_dumped": "EEPROM dumped to console",
- "rnode_detected": "RNode v{version} detected",
- "provision_success": "device has been provisioned!",
- "hash_success": "firmware hash has been set!",
- "tnc_enabled": "TNC mode has been enabled!",
- "tnc_disabled": "TNC mode has been disabled!",
- "bluetooth_enabled": "Bluetooth has been enabled!",
- "bluetooth_disabled": "Bluetooth has been disabled!",
- "bluetooth_pairing_pin": "Bluetooth Pairing Pin: {pin}",
- "bluetooth_pairing_started": "Pairing mode started (30s)",
- "rotation_updated": "Rotation updated",
- "reconditioning_started": "Reconditioning started",
- "diagnostics_healthy": "Diagnostics: device looks healthy.",
- "diagnostics_issues": "Diagnostics: {count} issue(s) detected."
- },
- "simple": "Simple",
- "advanced": "Advanced",
- "original": "Original",
- "open_original_tab": "Open the original flasher in a new tab",
- "bluetooth": "Bluetooth",
- "preparing_firmware": "Preparing firmware...",
- "uploading": "Uploading: {percentage}%",
- "select_firmware_file": "Or select a firmware file (.zip)",
- "wifi_help": "The IP address of your RNode on the local network.",
- "run_diagnostics": "Diagnostics",
- "support": {
- "serial": {
- "title": "USB serial is unavailable",
- "polyfill_not_loaded": "USB serial polyfill has not been loaded yet.",
- "android_webview_no_serial": "Android does not support Web Serial in this view. Use a desktop browser or the built-in flasher.",
- "browser_unsupported": "This browser does not support Web Serial. Try Chrome, Edge, or another Chromium-based browser.",
- "android_use_chrome": "On Android, opening this page in Chrome lets you use Web Serial via WebUSB.",
- "unknown": "USB serial cannot be initialised in this environment."
- },
- "bluetooth": {
- "title": "Bluetooth is unavailable",
- "android_bridge_not_implemented": "Web Bluetooth is not available here. Use the OS Bluetooth settings or a different browser.",
- "browser_unsupported": "This browser does not support Web Bluetooth. Try Chrome or Edge.",
- "insecure_context": "Web Bluetooth requires a secure context (HTTPS or localhost).",
- "requires_https": "Open the flasher over HTTPS or via localhost to enable Bluetooth.",
- "unknown": "Bluetooth cannot be initialised in this environment."
- },
- "actions": {
- "load_polyfill": "Load polyfill",
- "request_bluetooth": "Allow Bluetooth",
- "open_settings": "Open settings",
- "polyfill_loading": "Loading USB serial polyfill...",
- "bluetooth_requested": "Bluetooth permission requested."
- }
- },
- "diagnostics": {
- "title": "Device Diagnostics",
- "healthy": "Healthy",
- "needs_attention": "Needs attention",
- "issues_detected": "Issues detected",
- "firmware_version": "Firmware",
- "platform": "Platform",
- "board": "Board",
- "provisioned": "Provisioned",
- "product": "Product",
- "model": "Model",
- "firmware_hash": "FW hash",
- "target_hash": "Target hash",
- "suggestions": {
- "not_provisioned": "EEPROM is not provisioned. Run step 3 (Provision EEPROM).",
- "firmware_hash_missing": "Firmware hash is missing. Run step 4 (Set Firmware Hash).",
- "firmware_hash_mismatch": "Firmware hash mismatch. Re-flash the firmware and re-run step 4.",
- "product_mismatch": "Selected product does not match the connected device.",
- "no_firmware_version": "Could not read firmware version. Try reconnecting the device.",
- "detect_failed": "Could not detect this RNode. Verify cable, port, and power.",
- "read_timeout": "Device did not respond in time. Try reconnecting or rebooting it."
- }
- }
- },
- "micron_editor": {
- "title": "Micron Editor",
- "description": "Write Nomad Network Micron pages with a live preview.",
- "editor": "Editor",
- "preview": "Preview",
- "save": "Save",
- "edit": "Edit",
- "view_preview": "View Preview",
- "placeholder": "Enter your Micron markup here...",
- "reset": "Reset",
- "new_tab": "New Tab",
- "rename_tab": "Rename Tab",
- "confirm_reset": "Are you sure you want to reset the editor? All your tabs and content will be lost.",
- "confirm_delete_tab": "Are you sure you want to delete this tab?",
- "main_tab": "Main",
- "guide_tab": "NomadNet Guide",
- "parser_by": "Using Micron Parser by",
- "wasm_active": "WASM engine active (fast parser)",
- "wasm_inactive": "JS engine active (slow parser, click for WASM)",
- "publish_prompt_name": "index.mu already exists on \"{server}\". Enter a page name (without .mu):",
- "publish_published": "Published \"{page}\" to {server}",
- "publish_failed": "Failed to publish page",
- "publish_all_prompt_server": "Enter server name to publish all tabs to:\n\nAvailable: {servers}",
- "publish_server_not_found": "Server not found: {server}",
- "publish_all_done": "Published {published}/{total} tabs to {server}"
- },
- "reticulum_config_editor": {
- "title": "Reticulum Config Editor",
- "description": "Edit the main Reticulum (RNS) configuration file inside MeshChatX. Restart RNS to apply changes.",
- "info": "This is the raw Reticulum config file. Saving will not apply changes until RNS is restarted.",
- "loading": "Loading config...",
- "reload": "Reload",
- "save": "Save",
- "saving": "Saving...",
- "saved": "Reticulum config saved",
- "discard": "Discard",
- "unsaved": "Unsaved changes",
- "restore_defaults": "Restore defaults",
- "restoring": "Restoring defaults...",
- "restored": "Reticulum config restored to defaults",
- "confirm_restore": "Restore the Reticulum config to defaults? Your current config will be overwritten.",
- "confirm_leave": "You have unsaved changes. Leave anyway?",
- "restart_required": "Restart required",
- "restart_description": "Reticulum must be restarted for your config changes to take effect.",
- "restart_now": "Restart RNS now",
- "restart_done": "Reticulum restarted",
- "failed_load": "Failed to load Reticulum config",
- "failed_save": "Failed to save Reticulum config",
- "failed_restore": "Failed to restore Reticulum config",
- "failed_restart": "Failed to restart Reticulum",
- "tips_title": "Tips",
- "tip_sections": "The config must contain [reticulum] and [interfaces] sections.",
- "tip_restart": "Use the Restart RNS button or the Interfaces page to apply changes.",
- "tip_mobile": "Useful whenever you prefer not to edit the file in another program on disk."
- },
- "paper_message": {
- "title": "Paper Message",
- "description": "Encode LXMF messages as QR codes or scan codes back into the app."
- },
- "mesh_server": {
- "title": "Mesh Server",
- "description": "Publish Micron pages and small files for mesh peers to open."
- },
- "rns_tunnel": {
- "title": "RNS Tunnel",
- "description": "Carry IP traffic over Reticulum links (in development)."
- },
- "rns_filesync": {
- "title": "File Sync",
- "description": "Keep a shared folder in sync with mesh friends, like Syncthing over Reticulum."
- },
- "bots": {
- "title": "LXMFy Bots",
- "description": "Run simple LXMF bots (echo, notes, reminders) when the LXMFy add-on is installed."
- }
- },
- "ping": {
- "title": "Ping Mesh Peers",
- "description": "Only destinations that expose the ping app type will answer.",
- "destination_hash": "Destination Hash",
- "timeout_seconds": "Ping Timeout (seconds)",
- "start_ping": "Start Ping",
- "stop": "Stop",
- "clear_results": "Clear Results",
- "drop_path": "Drop Path",
- "status": "Status",
- "running": "Running",
- "idle": "Idle",
- "last_rtt": "Last RTT",
- "last_error": "Last Error",
- "console_output": "Console Output",
- "streaming_responses": "Streaming seq responses in real time",
- "no_pings_yet": "No pings yet. Start a run to collect RTT data.",
- "invalid_hash": "Invalid destination hash!",
- "timeout_must_be_number": "Timeout must be a number!"
- },
- "relay_chat": {
- "title": "Relay Chat",
- "subtitle": "Reticulum Relay Chat hubs and rooms",
- "hubs": "Hubs",
- "add_hub": "Add Hub",
- "add_hub_title": "Add a Relay Chat Hub",
- "hub_hash": "Hub Destination Hash",
- "hub_hash_placeholder": "Enter the hub identity hash",
- "hub_name": "Hub Name (optional)",
- "hub_name_placeholder": "My favourite hub",
- "dest_name": "Destination Aspect (optional)",
- "advanced": "Advanced",
- "no_hubs": "No hubs configured yet. Add one to get started.",
- "no_rooms": "No rooms joined. Join a room to start chatting.",
- "available_rooms": "Available Rooms",
- "join": "Join",
- "no_room_selected": "Select a room to view messages.",
- "load_previous": "Load earlier messages",
- "rooms": "Rooms",
- "members": "Members",
- "join_room": "Join Room",
- "join_room_placeholder": "room name",
- "join_room_key_placeholder": "Room key (if required)",
- "room_key_prompt": "Enter the key for #{room}",
- "bad_key": "Wrong room key. Enter the correct key to join.",
- "join_requested": "Join request sent",
- "leave_room": "Leave Room",
- "clear_messages": "Clear Messages",
- "connect": "Connect",
- "disconnect": "Disconnect",
- "remove_hub": "Remove Hub",
- "remove_hub_confirm": "Remove this hub and all of its local history?",
- "leave_room_confirm": "Leave this room?",
- "clear_messages_confirm": "Clear all messages in this room?",
- "message_placeholder": "Write a message...",
- "send": "Send",
- "settings": "Hub Settings",
- "auto_reconnect": "Auto reconnect",
- "auto_list": "Auto list rooms",
- "auto_who": "Auto list members",
- "nickname": "Nickname override",
- "nickname_placeholder": "Leave blank to use display name",
- "save": "Save",
- "status_disconnected": "Disconnected",
- "status_connecting": "Connecting",
- "status_connected": "Connected",
- "status_failed": "Failed",
- "motd": "Message of the day",
- "system": "System",
- "hub_added": "Hub added",
- "hub_removed": "Hub removed",
- "settings_saved": "Settings saved",
- "messages_cleared": "Messages cleared",
- "joined_room": "Joined room",
- "left_room": "Left room",
- "invalid_hub_hash": "Enter a valid hub hash",
- "send_failed": "Failed to send message",
- "action_failed": "Action failed",
- "room_required": "A room name is required",
- "tab_connect": "Connect",
- "tab_host": "Host",
- "host_title": "Hosted Hubs",
- "host_subtitle": "Run your own Relay Chat hubs and create rooms others can join.",
- "create_hub": "Create Hub",
- "create_hub_title": "Create a Relay Chat Hub",
- "no_hosted_hubs": "No hosted hubs yet. Create one to start relaying chat.",
- "copy_hash": "Copy destination hash",
- "hash_copied": "Destination hash copied",
- "share_hub": "Share hub link",
- "share_copied": "Relay share link copied",
- "host_join_as_client": "Join this hub as a client",
- "host_leave_as_client": "Leave this hub as a client",
- "host_joined_as_client": "Joined hosted hub as client",
- "host_start": "Start",
- "host_status_running": "Running",
- "host_status_stopped": "Stopped",
- "host_hub_not_running": "Start the hub before managing rooms or members.",
- "host_stop": "Stop",
- "host_announce": "Announce now",
- "host_announced": "Announced",
- "host_delete": "Delete hub",
- "host_clients": "clients",
- "host_rooms": "rooms",
- "host_private": "private",
- "host_delete_room": "Delete room",
- "host_no_rooms": "No rooms yet.",
- "host_room_name": "room name",
- "host_room_topic": "topic (optional)",
- "host_add_room": "Add Room",
- "host_greeting": "Greeting (optional)",
- "host_greeting_placeholder": "Welcome message shown on connect",
- "host_announce_periodically": "Announce on the network",
- "host_hub_created": "Hub created",
- "host_delete_hub_confirm": "Delete this hosted hub? Connected clients will be disconnected.",
- "host_hub_deleted": "Hub deleted",
- "host_room_created": "Room created",
- "host_room_key_placeholder": "Room key (optional)",
- "host_room_keyed": "This room requires a key",
- "host_room_unkeyed": "This room has no key",
- "host_set_room_key": "Set key",
- "host_change_room_key": "Change key",
- "host_clear_room_key": "Clear key",
- "host_room_key_prompt": "Enter a new key for #{room}",
- "host_room_key_required": "A room key is required",
- "host_room_key_saved": "Room key saved",
- "host_clear_room_key_confirm": "Remove the key from this room? Anyone will be able to join.",
- "host_room_key_cleared": "Room key cleared",
- "host_delete_room_confirm": "Delete this room?",
- "host_room_deleted": "Room deleted",
- "host_members_all_title": "Connected clients: {hub}",
- "host_members_room_title": "#{room}: {hub}",
- "host_member_rooms": "Rooms",
- "host_members_search": "Search members...",
- "host_rooms_search": "Search rooms...",
- "host_rooms_search_empty": "No rooms match your search.",
- "host_moderation_uptime": "{time} uptime",
- "host_moderation_members": "{count} members",
- "host_members_select": "Select a member to view messages and moderate",
- "host_no_messages": "No messages from this user in the log yet",
- "host_ban_hub": "Ban from hub",
- "host_kick_confirm": "Kick {name} from #{room}?",
- "host_kick_pick_room": "Kick {name} from which room? Enter one of: {rooms}",
- "host_kick_no_room": "This user is not in any room, so they cannot be kicked.",
- "host_kick_room_invalid": "Room not found. Choose one of the listed rooms.",
- "host_cannot_moderate_self": "You cannot moderate your own connection.",
- "host_ban_confirm": "Ban {name} from this hub?",
- "host_room_ban_confirm": "Ban {name} from #{room}?",
- "host_manage_rooms": "Manage rooms",
- "host_moderate": "Moderate hub",
- "host_moderation_title": "Moderation",
- "host_moderation_title_hub": "Moderation: {hub}",
- "host_moderation_title_room": "Moderation: {hub} (#{room})",
- "host_moderation_tab_rooms": "Rooms",
- "host_moderation_tab_members": "Members",
- "host_moderation_hub_missing": "Hub not found.",
- "host_rooms_modal_title": "Rooms: {hub}",
- "host_rooms_select": "Select a room to view activity",
- "host_room_activity": "Recent activity",
- "host_no_activity": "No recent activity in this room",
- "hub_icon": "Hub icon",
- "hub_icon_choose": "Choose icon",
- "hub_icon_picker_title": "Choose hub icon",
- "hub_icon_search": "Search {count} icons...",
- "hub_icon_no_results": "No icons match your search.",
- "hub_icon_search_limit": "Showing first {count} results. Refine your search.",
- "hub_icon_reset_default": "Use default icon",
- "disabled_message": "Relay Chat is turned off in Settings. Enable it under Appearance to use hubs and hosting.",
- "ctx_add_hub": "Add hub",
- "ctx_add_room": "Join room",
- "ctx_connect_hub": "Connect",
- "ctx_disconnect_hub": "Disconnect",
- "ctx_hub_settings": "Hub settings",
- "ctx_copy_hub_address": "Copy hub address",
- "ctx_share_hub": "Share hub",
- "ctx_share_room": "Share room",
- "ctx_remove_hub": "Remove hub",
- "ctx_leave_room": "Leave room",
- "ctx_reply_quote": "Reply with quote",
- "ctx_mention_user": "Mention user",
- "ctx_copy_message": "Copy message",
- "ctx_kick_user": "Kick user",
- "ctx_ban_user": "Ban user",
- "tab_discovery": "Discover",
- "discovery_title": "Discovered Hubs",
- "discovery_subtitle": "Relay Chat hubs heard announcing on the network.",
- "discovery_empty": "No hubs discovered yet. They appear here as they announce.",
- "discovery_search": "Search {count} hubs",
- "discovery_add": "Add",
- "discovery_added": "Hub added from discovery",
- "discovery_open": "Open",
- "discovery_browse": "Browse discovered hubs",
- "announced_ago": "Announced {time} ago",
- "hops_away": "{count} hops",
- "back": "Back",
- "refresh": "Refresh",
- "members_title": "Members",
- "members_online": "Online",
- "members_offline": "Offline",
- "no_members": "No one here yet",
- "show_members": "Show members",
- "hide_members": "Hide members",
- "discovery_refreshed": "Discovery refreshed",
- "collapse_sidebar": "Collapse sidebar",
- "expand_sidebar": "Expand sidebar",
- "add_hub_card": "Add a hub",
- "hub_display_name": "Display name",
- "revert_hub_name": "Revert to announced name",
- "search_messages": "Search",
- "search_messages_placeholder": "Search (AND, OR, DATE:2026-06-02)...",
- "search_empty_hint": "Search this room",
- "search_syntax_hint": "Examples: hello world · alice OR bob · DATE:today · DATE:2026-06-02",
- "search_no_results": "No messages match your search",
- "members_search_placeholder": "Search members...",
- "members_search_no_results": "No members match your search.",
- "popout_channel": "Open in new window",
- "new_message_toast": "New message in #{room}",
- "host_users": "users",
- "host_announce_interval": "Announce interval (minutes)",
- "host_announce_interval_hint": "Every {minutes} minutes",
- "host_hub_settings": "Hub settings",
- "presence_joined": "{count} joined",
- "presence_left": "{count} left",
- "presence_events": "{count} join/leave events",
- "presence_connection": "{count} connection events"
- },
- "rncp": {
- "file_transfer": "File Transfer",
- "title": "RNCP - Reticulum Network Copy",
- "description": "Send, listen, or fetch files over Reticulum using RNCP (chunked transfers, optional compression).",
- "usage_steps": "How to use RNCP:",
- "step_1": "1. To **receive** a file, go to the **Listen** tab, enter allowed identity hashes (who can send to you), and click **Start Listening**.",
- "step_2": "2. To **send** a file, get the **Destination Hash** of a listening node, enter the **Local File Path**, and click **Send File**.",
- "step_3": "3. To **fetch** a file, get the **Destination Hash** of a node listening with 'Allow Fetch' enabled, enter the **Remote File Path**, and click **Fetch File**.",
- "send_file": "Send File",
+ "_languageName": "English",
+ "bots": {
+ "title": "LXMFy Bots",
+ "description": "Run simple LXMF bots (echo, notes, reminders) when the LXMFy add-on is installed.",
+ "bot_framework": "Bot Framework",
+ "lxmfy_not_detected": "LXMFy not detected",
+ "lxmfy_not_detected_desc": "To use bots, you must install the LXMFy package:",
+ "install_via_pip": "Install via pip",
+ "create_new_bot": "Create New Bot",
+ "running_bots": "Running Bots",
+ "no_bots_running": "No bots are currently running.",
+ "select": "Select",
+ "start_bot": "Start Bot",
+ "stop_bot": "Stop Bot",
+ "restart_bot": "Restart Bot",
+ "saved_bots": "Saved Bots",
+ "bot_name": "Bot Name",
+ "cancel": "Cancel",
+ "bot_started": "Bot started successfully",
+ "bot_stopped": "Bot stopped",
+ "start_pending": "Starting bot… LXMF and Reticulum can take a moment.",
+ "stop_pending": "Stopping bot…",
+ "restart_pending": "Restarting bot… this may take a moment.",
+ "bot_ready": "Bot is up and running.",
+ "failed_to_start": "Failed to start bot",
+ "failed_to_stop": "Failed to stop bot",
+ "delete_bot": "Delete Bot",
+ "export_identity": "Export Identity",
+ "bot_deleted": "Bot deleted successfully",
+ "failed_to_delete": "Failed to delete bot",
+ "more_bots_coming": "More bots coming soon!",
+ "chat_with_bot": "Chat",
+ "lxmf_address": "LXMF address",
+ "last_announce": "Last announce",
+ "never_announced": "Not seen on this node yet",
+ "address_pending": "Not available until the bot has started once",
+ "status_running": "Running",
+ "status_stopped": "Stopped",
+ "force_announce": "Announce now",
+ "edit_name": "Edit name",
+ "bot_renamed": "Bot name saved",
+ "rename_failed": "Could not save name",
+ "name_required": "Enter a name",
+ "announce_triggered": "Announce requested",
+ "announce_failed": "Could not request announce",
+ "last_error_heading": "Last error",
+ "view_process_log": "Process log (debug)",
+ "process_log_title": "Bot process log",
+ "process_log_empty": "No log output yet for this bot.",
+ "process_log_truncated": "Only the end of the log is shown. The full file on disk is larger.",
+ "process_log_loading": "Loading log…",
+ "process_log_failed": "Could not load process log",
+ "process_log_copied": "Log copied to clipboard",
+ "copy_process_log": "Copy log"
+ },
+ "app": {
+ "name": "Reticulum MeshChatX",
+ "sync_messages": "Sync Messages",
+ "compose": "Compose",
+ "messages": "Messages",
+ "contacts": "Contacts",
+ "relay_chat": "Relay Chat",
+ "nomad_network": "Nomad Network",
+ "map": "Map",
+ "archives": "Archives",
+ "propagation_nodes": "Propagation Nodes",
+ "network_visualiser": "Network Visualiser",
+ "interfaces": "Interfaces",
+ "tools": "Tools",
+ "settings": "Settings",
+ "identities": "Identities",
+ "documentation": "Documentation",
+ "about": "About",
+ "changelog_title": "What's New",
+ "do_not_show_again": "Do not show again for this version",
+ "do_not_show_ever": "Do not show ever again",
+ "tutorial_title": "Getting Started",
+ "tutorial_welcome": "Welcome to MeshChatX",
+ "tutorial_connect": "Connect to the Mesh",
+ "tutorial_finish": "Ready to Roll!",
+ "tutorial_restart_required": "Restart Required",
+ "tutorial_docker_note": "If you're running in Docker, ensure you restart the container.",
+ "my_identity": "My Identity",
+ "identity_hash": "Identity Hash",
+ "lxmf_address": "LXMF Address",
+ "announce": "Announce",
+ "announce_now": "Announce Now",
+ "show_qr": "Show QR Code",
+ "failed_announce": "failed to announce",
+ "announce_sent": "Announcement sent",
+ "notifications": "Notifications",
+ "notification_sound_settings": "Message Notification Sound",
+ "notification_sound_settings_description": "Play a custom sound when you receive a new message while MeshChat is open. Upload a sound file first, then enable playback.",
+ "enable_notification_sound": "Enable notification sound",
+ "enable_notification_sound_description": "Plays your chosen sound for incoming messages on desktop (Windows, macOS, and Linux).",
+ "notification_sound_volume": "Notification volume",
+ "notification_sound_default": "Default notification sound",
+ "notification_sound_primary_default": "Primary uploaded sound",
+ "notification_sounds": "Uploaded sounds",
+ "notification_sound_upload": "Upload sound",
+ "notification_sound_uploading": "Uploading...",
+ "notification_sound_none_uploaded": "No notification sound uploaded yet. Upload an audio file to get started.",
+ "notification_sound_primary": "Primary",
+ "notification_sound_preview": "Preview sound",
+ "notification_sound_set_primary": "Set primary",
+ "notification_sound_remove": "Remove sound",
+ "notification_sound_uploaded": "Notification sound uploaded",
+ "notification_sound_upload_failed": "Failed to upload notification sound",
+ "notification_sound_deleted": "Notification sound deleted",
+ "notification_sound_delete_failed": "Failed to delete notification sound",
+ "notification_sound_primary_set": "Primary notification sound updated",
+ "notification_sound_primary_set_failed": "Failed to set primary notification sound",
+ "notification_sound_preview_failed": "Could not play notification sound. Try again after interacting with the app.",
+ "last_announced": "Last announced: {time}",
+ "last_announced_never": "Last announced: Never",
+ "display_name_placeholder": "Display Name",
+ "profile": "Profile",
+ "manage_identity": "Manage your identity, transport participation and LXMF defaults.",
+ "theme": "Theme",
+ "theme_mode": "{mode} mode",
+ "transport": "Transport",
+ "enabled": "Enabled",
+ "disabled": "Disabled",
+ "propagation": "Propagation",
+ "local_node_running": "Local node running",
+ "client_only": "Client-only",
+ "copy": "Copy",
+ "appearance": "Appearance",
+ "appearance_description": "Switch between light and dark presets anytime.",
+ "messages_sidebar_position": "Messages sidebar",
+ "messages_sidebar_position_left": "Left",
+ "messages_sidebar_position_right": "Right",
+ "ui_transparency": "Background transparency",
+ "ui_transparency_description": "Increases transparency on the main background. Side panels stay solid for readability.",
+ "ui_glass_enabled": "Glass effect",
+ "ui_glass_enabled_description": "Adds a frosted blur effect to cards. When off, cards use solid colors.",
+ "messages_multi_pane_enabled": "Messages split view",
+ "messages_multi_pane_enabled_description": "Show multiple conversations side by side on larger screens. Drag a conversation onto a pane to open it there.",
+ "nomad_tabs_enabled": "NomadNet browser tabs",
+ "nomad_tabs_enabled_description": "Open multiple NomadNet pages in tabs on larger screens.",
+ "rrc_enabled": "Relay Chat",
+ "rrc_enabled_description": "Enable relay chat, hub discovery, and hosting. When off, relay chat is hidden and hub announces are ignored.",
+ "rrc_unread_badges_enabled": "Relay Chat unread badges",
+ "rrc_unread_badges_enabled_description": "Show unread message counters on hubs and rooms. Mentions in the nav badge are unaffected.",
+ "reset_appearance_defaults": "Reset appearance to defaults",
+ "light_theme": "Light Theme",
+ "dark_theme": "Dark Theme",
+ "live_preview": "Live preview updates instantly.",
+ "realtime": "Realtime",
+ "transport_mode": "Transport Mode",
+ "transport_description": "Relay paths and traffic for nearby peers.",
+ "enable_transport_mode": "Enable Transport Mode",
+ "transport_toggle_description": "Route announces, respond to path requests and help your mesh stay online.",
+ "share_reticulum_instance": "Share Reticulum Instance",
+ "share_reticulum_instance_description": "Make this MeshChatX Reticulum instance available to other local programs (Sideband-style shared instance). Other apps can use connectivity without Transport enabled.",
+ "obfuscate_hops": "Obfuscate Hops",
+ "obfuscate_hops_description": "When enabled, Reticulum applies a random local hop delta (RNS local_hops_delta) so outbound traffic does not advertise a true zero-hop origin to the wider mesh.",
+ "respond_to_probes": "Respond to Probes",
+ "respond_to_probes_description": "Allow this instance to answer Reticulum probe requests from the network.",
+ "enable_remote_management": "Enable Remote Management",
+ "enable_remote_management_description": "Allow remote management of this Reticulum instance when configured in the RNS config.",
+ "remote_management_allowed": "Remote management allow-list",
+ "remote_management_allowed_description": "Identity hashes allowed to query this instance remotely with rnstatus/rnpath. One 32-character hex hash per line.",
+ "remote_management_allowed_placeholder": "9fb6d773498fb3feda407ed8ef2c3229",
+ "remote_management_allowed_save": "Save allow-list",
+ "shared_instance_type": "Shared Instance Type",
+ "shared_instance_type_default": "Platform default",
+ "shared_instance_type_description": "Use TCP when domain sockets are unavailable (common on Android). Leave default otherwise.",
+ "instance_name": "Instance Name",
+ "instance_name_description": "Isolate multiple shared instances on one system. Default is usually fine.",
+ "rpc_config": "RPC Access",
+ "rpc_config_description": "Copy these lines into another program's [reticulum] section for full shared-instance RPC access (management, interface status, path info).",
+ "rpc_config_unavailable": "RPC key is not available yet. Start Reticulum as a shared instance first.",
+ "rpc_config_copied": "RPC config copied to clipboard",
+ "rpc_key_hidden": "Hidden. Tap to show",
+ "rpc_key_show": "Show RPC key",
+ "rpc_key_hide": "Hide RPC key",
+ "copy_rpc_config": "Copy RPC Config",
+ "connected_to_shared_instance": "This MeshChatX process is attached to an external shared Reticulum instance. Edit that instance's config to change share/RPC settings.",
+ "copy_failed": "Failed to copy to clipboard",
+ "requires_restart": "Requires restart after toggling.",
+ "show_community_interfaces": "Show Community Interfaces",
+ "community_interfaces_description": "Show community-maintained presets when adding new interfaces.",
+ "refresh_community_interfaces": "Refresh from directory.rns.recipes",
+ "refresh_community_interfaces_busy": "Refreshing…",
+ "reliability": "Reliability",
+ "lxmf_settings_eyebrow": "LXMF",
+ "privacy_eyebrow": "Privacy",
+ "privacy_data_title": "Data & device",
+ "privacy_data_description": "Manage local message history, telemetry, and other privacy options. Deleting here only affects this device.",
+ "privacy_data": "Data & device",
+ "privacy_subsection_device": "This device",
+ "privacy_subsection_telemetry": "Mesh telemetry",
+ "privacy_mode_enabled": "Privacy mode (block external HTTP/HTTPS)",
+ "privacy_mode_description": "Blocks map tiles, geocoding, LibreTranslate, firmware downloads, and other outbound HTTP/HTTPS from the app. The browser Content Security Policy is tightened to same-origin only.",
+ "multi_session_warning_enabled": "Warn when multiple sessions are connected",
+ "multi_session_warning_description": "Show a warning toast when two or more browsers or devices are connected to this MeshChatX instance at the same time.",
+ "multi_session_warning": "Multiple active sessions ({count}). Another browser or device is connected to this MeshChatX instance.",
+ "screen_security_enabled": "Screen security (block capture)",
+ "screen_security_description": "Uses a Windows display-affinity flag so MeshChatX is omitted from screenshots, screen recording, and Windows Recall. The window still looks normal on your monitor.",
+ "screen_security_description_short": "Hide this window from screenshots, recorders, and Windows Recall.",
+ "screen_security_drm_eyebrow": "Windows DRM / screen capture protection",
+ "screen_security_drm_note": "This reuses the same OS content-protection flag media apps use for DRM. It is not a hard guarantee: a camera pointed at the display can still capture content, and some accessibility tools may need this setting off.",
+ "screen_security_disable_confirm": "Turn off screen security? Windows Recall and other apps will be able to capture MeshChatX again.",
+ "screen_security_enabled_toast": "Screen security enabled",
+ "screen_security_disabled_toast": "Screen security disabled",
+ "web_exposure_title": "Network exposure",
+ "web_exposure_description": "The web UI bind address is set at startup via --host or MESHCHAT_HOST. Restrict who can reach it when not bound to loopback.",
+ "web_listen_address": "Current bind address",
+ "web_listen_https": "HTTPS",
+ "web_exposure_warning_title": "This instance is reachable beyond localhost",
+ "web_exposure_warning_body": "Binding to a non-loopback address exposes the web UI on your network. Enable authentication, restrict the port with a firewall, and prefer access over a VPN you control.",
+ "web_exposure_check_auth": "Authentication enabled",
+ "web_exposure_check_auth_off": "Authentication is disabled",
+ "web_exposure_check_firewall": "I restricted port access with a firewall or bind rules",
+ "web_exposure_check_vpn": "Remote access is only via a VPN I control",
+ "web_ui_ip_allowlist": "Web UI IP allowlist",
+ "web_ui_ip_allowlist_description": "Optional comma-separated IPs or CIDR ranges (for example 127.0.0.1/32, 192.168.1.0/24). Empty allows all clients.",
+ "web_ui_ip_allowlist_placeholder": "127.0.0.1/32, ::1/128, 192.168.0.0/16",
+ "landlock_status": "Landlock sandbox (Linux)",
+ "landlock_active": "Active",
+ "landlock_inactive": "Not active",
+ "landlock_auto_enabled": "Enabled automatically on this Linux kernel",
+ "landlock_kernel_unsupported": "Kernel does not support Landlock (5.13+ required)",
+ "landlock_disabled_by_env": "Disabled via MESHCHAT_LANDLOCK=0",
+ "seccomp_status": "Seccomp-BPF sandbox (Linux)",
+ "seccomp_active": "Active",
+ "seccomp_inactive": "Not active",
+ "seccomp_auto_enabled": "Enabled automatically when libseccomp is available",
+ "seccomp_kernel_unsupported": "Seccomp-BPF unavailable (install libseccomp, or unsupported platform)",
+ "seccomp_disabled_by_env": "Disabled via MESHCHAT_SECCOMP=0",
+ "settings_map_eyebrow": "Map",
+ "messages_description": "Configure how MeshChat handles message delivery failures. Control automatic retry behavior, attachment retransmission, and fallback mechanisms to ensure reliable message delivery across the mesh network.",
+ "auto_resend_title": "Auto resend when peer announces",
+ "auto_resend_description": "Failed messages automatically retry once the destination broadcasts again.",
+ "retry_attachments_title": "Allow retries with attachments",
+ "retry_attachments_description": "Large payloads will also be retried (useful when both peers have high limits).",
+ "auto_fallback_title": "Auto fall back to propagation node",
+ "auto_fallback_description": "Failed direct deliveries are queued on your preferred propagation node.",
+ "inbound_stamps_required_title": "Require inbound stamps",
+ "inbound_stamps_required_description": "When off, direct messages to you do not require proof-of-work stamps. When on, set the stamp cost below. Higher values mean more work for senders.",
+ "inbound_stamp_cost": "Inbound Message Stamp Cost",
+ "inbound_stamp_description": "Proof-of-work difficulty for direct messages. Range: 1-254. Default: 8.",
+ "flood_protection": "Flood Protection",
+ "flood_protection_description": "Automatically raise the inbound stamp cost when receiving too many messages per minute from many sources. This makes coordinated spam attacks computationally expensive while keeping normal conversations affordable.",
+ "flood_protection_enabled": "Enable auto-adjusting stamp cost",
+ "flood_threshold": "Messages per minute threshold",
+ "flood_max_stamp_cost": "Maximum stamp cost during flood",
+ "flood_cooldown": "Cooldown before lowering cost (seconds)",
+ "local_message_auto_delete_title": "Delete old messages on this device",
+ "local_message_auto_delete_description": "Removes messages from the local database after the age you set. This only affects this device and no data is deleted from the network.",
+ "local_message_auto_delete_age": "Delete messages older than",
+ "local_message_auto_delete_unit_aria": "Time unit for message retention",
+ "local_message_auto_delete_unit_days": "days",
+ "local_message_auto_delete_unit_months": "months (30 days each)",
+ "local_message_auto_delete_month_note": "Months are counted as 30 days each. Disabled by default.",
+ "browse_nodes": "Browse Nodes",
+ "propagation_nodes_description": "Keep conversations flowing even when peers are offline.",
+ "nodes_info_1": "Propagation nodes hold messages securely until recipients sync again.",
+ "nodes_info_2": "Nodes peer with each other to distribute encrypted payloads.",
+ "nodes_info_3": "Most nodes retain data ~30 days, then discard undelivered items.",
+ "run_local_node": "Run a local propagation node",
+ "run_local_node_description": "MeshChat will announce and maintain a node using this local destination hash.",
+ "preferred_propagation_node": "Preferred Propagation Node",
+ "preferred_node_placeholder": "Destination hash, e.g. a39610c89d18bb48c73e429582423c24",
+ "fallback_node_description": "Messages fallback to this node whenever direct delivery fails.",
+ "auto_select_node": "Auto-select best node",
+ "auto_select_node_description": "Automatically find and switch to the best available propagation node based on network hops.",
+ "auto_select_using_label": "Using propagation node",
+ "auto_select_pending": "No propagation node selected yet. The app will choose one when available.",
+ "incoming_message_size": "Incoming message size",
+ "incoming_message_size_description": "Maximum size for one direct LXMF delivery you will accept.",
+ "incoming_message_size_1mb": "1 MB",
+ "incoming_message_size_10mb": "10 MB",
+ "incoming_message_size_25mb": "25 MB",
+ "incoming_message_size_50mb": "50 MB",
+ "incoming_message_size_1gb": "1 GB",
+ "incoming_message_size_custom": "Custom",
+ "incoming_message_size_unit_mb": "MB",
+ "incoming_message_size_unit_gb": "GB",
+ "auto_sync_interval": "Auto Sync Interval",
+ "last_synced": "Last synced {time} ago.",
+ "last_synced_never": "Last synced: never.",
+ "propagation_stamp_cost": "Propagation Node Stamp Cost",
+ "propagation_stamp_description": "Require proof-of-work stamps for messages sent through your node. Higher values require more work. Range: 13-254. Default: 16. **Note:** Changing this requires a restart.",
+ "propagation_sequential_validation": "Sequential PN Stamp Validation",
+ "propagation_sequential_validation_description": "Validate inbound propagation sync stamp batches one at a time. Other peers receive a throttle response while a batch is validating. Disable only on fast hosts.",
+ "propagation_static_peers_bypass_sequential": "Static Peers Bypass Sequential Validation",
+ "propagation_static_peers_bypass_sequential_description": "Allow configured static peers to sync immediately even when another stamp batch is already validating.",
+ "propagation_max_inbound_syncs": "Max Concurrent Inbound Syncs",
+ "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3.",
+ "language": "Language",
+ "select_language": "Select your preferred language.",
+ "tagline": "All-in-one Reticulum client",
+ "open": "Open",
+ "identity": "Identity",
+ "lxmf_address_hash": "LXMF Address Hash",
+ "propagation_node_status": "Propagation Node Status",
+ "last_sync": "Last Sync: {time} ago",
+ "last_sync_never": "Last Sync: never",
+ "syncing": "Syncing...",
+ "synced": "Synced",
+ "not_synced": "Not Synced",
+ "not_configured": "Not Configured",
+ "toggle_source": "Toggle Source Code",
+ "audio_calls": "Telephone",
+ "calls": "Calls",
+ "status": "Status",
+ "active_call": "Active Call",
+ "incoming": "Incoming",
+ "outgoing": "Outgoing",
+ "call": "Call",
+ "calls_plural": "Calls",
+ "hop": "hop",
+ "hops_plural": "hops",
+ "hung_up_waiting": "Hung up, waiting for call...",
+ "view_incoming_calls": "View Incoming Calls",
+ "hangup_all_calls": "Hangup all Calls",
+ "clear_history": "Clear History",
+ "no_active_calls": "No active calls",
+ "incoming_call": "Incoming call...",
+ "outgoing_call": "Outgoing call...",
+ "call_active": "Active",
+ "call_ended": "Ended",
+ "propagation_node": "Propagation Node",
+ "sync_now": "Sync Now",
+ "setting_auto_saved": "Setting {label} auto-saved",
+ "backup_max_count": "Automatic backup limit",
+ "message_font_size": "Message font size",
+ "message_icon_size": "Icon size",
+ "detailed_outbound_send_status": "Detailed outbound send status",
+ "detailed_outbound_send_status_description": "Show detailed status like pathfinding, stamp solving, and technical tooltips while sending. When off, a simple checkmark is shown.",
+ "outbound_transfer_progress_enabled": "Show transfer progress bar",
+ "outbound_transfer_progress_enabled_description": "Show a progress bar with transfer speed, hops, and elapsed time below outbound messages while they are sending.",
+ "message_timestamp_grouping": "Group message timestamps",
+ "message_timestamp_grouping_description": "When on, messages are grouped by date and timestamps are simplified. When off, every message shows its exact time.",
+ "search_settings": "Search settings...",
+ "auto_resend": "Auto Resend",
+ "retry_attachments": "Retry Attachments",
+ "auto_fallback": "Auto Fallback",
+ "community_interfaces": "Community Interfaces",
+ "preferred_node": "Preferred Node",
+ "local_node": "Local Node",
+ "auto_sync": "Auto Sync",
+ "inbound_stamp_cost_label": "Inbound Stamp Cost",
+ "propagation_stamp_cost_label": "Propagation Stamp Cost",
+ "page_archiver": "Page Archiver",
+ "smart_crawler": "Smart Crawler",
+ "authentication": "Authentication",
+ "stranger_protection": "Stranger Protection",
+ "stranger_protection_description": "Control how the app handles messages and attachments from unknown peers.",
+ "block_stranger_attachments": "Block Attachments from Strangers",
+ "block_stranger_attachments_description": "Automatically remove file, image, and audio attachments from people not in your contacts. Text messages are still delivered.",
+ "block_all_from_strangers": "Block Everything from Strangers",
+ "block_all_from_strangers_description": "Ignore all messages, attachments, and calls from people not in your contacts. Nothing is delivered or saved.",
+ "show_unknown_contact_banner": "Show Unknown Contact Banner",
+ "show_unknown_contact_banner_description": "When enabled, a notice appears at the top of conversations with peers who are not in your contacts.",
+ "warn_on_stranger_links": "Warn Before Opening Stranger Links",
+ "warn_on_stranger_links_description": "Show a warning prompt before opening HTTP/HTTPS links sent by peers who are not in your contacts.",
+ "banishment": "Banishment",
+ "banishment_description": "Customize the visual effect shown for banished peers and nodes.",
+ "banished_effect_enabled": "Enable Banishment Effect",
+ "banished_effect_description": "Show a visual overlay on banished peers and nodes.",
+ "banished_text_label": "Banishment Text",
+ "banished_text_description": "Custom message to display on the overlay.",
+ "banished_color_label": "Banishment Color",
+ "banished_color_description": "The color used for the banishment effect.",
+ "desktop_open_calls_in_separate_window": "Open Calls in 2nd Window",
+ "desktop_open_calls_in_separate_window_description": "When a call is active, it will be shown in a separate pop-out window rather than inside the app.",
+ "desktop_hardware_acceleration_enabled": "Hardware Acceleration",
+ "desktop_hardware_acceleration_enabled_description": "Disable this if you experience flickering, black screens, or other graphical issues.",
+ "desktop_tray_enabled": "System tray integration",
+ "desktop_tray_enabled_description": "Keep a tray icon so MeshChatX can hide to the background instead of quitting.",
+ "desktop_close_behavior": "When closing the window",
+ "desktop_close_behavior_description": "Choose whether closing the window quits, hides to the tray/taskbar, or asks each time.",
+ "desktop_close_behavior_ask": "Ask every time",
+ "desktop_close_behavior_quit": "Quit application",
+ "desktop_close_behavior_background": "Keep running in background",
+ "desktop_close_behavior_background_no_tray": "Minimize to taskbar",
+ "translator": "Translator",
+ "translator_enabled": "Enable Translator",
+ "translator_description": "Enable translation features in conversations.",
+ "libretranslate_url": "LibreTranslate URL",
+ "libretranslate_url_description": "The URL where LibreTranslate API is accessible.",
+ "system": "System",
+ "reticulum_stack": "Reticulum Stack",
+ "reticulum_stack_description": "Manage the underlying Reticulum Network Stack.",
+ "reload_rns": "Reload RNS",
+ "reload_rns_description": "Deregister all destinations, stop the current RNS instance, and reload it. This is useful for applying configuration changes or restarting the mesh network stack without restarting the entire application.",
+ "reloading_rns": "Reloading RNS...",
+ "restart_rns": "Restart RNS",
+ "reloaded_rns": "Reticulum reloaded successfully",
+ "announce_interval": "Announce Interval",
+ "stop_sync_confirm": "Are you sure you want to stop syncing?",
+ "cancel_inbound_deliveries": "Cancel incoming transfers",
+ "cancel_inbound_deliveries_count": "Cancel incoming ({count})",
+ "cancel_inbound_confirm": "Cancel {count} incoming LXMF delivery transfer(s)? Large messages currently downloading will stop.",
+ "cancel_inbound_done": "Cancelled {count} incoming delivery transfer(s).",
+ "cancel_inbound_failed": "Failed to cancel incoming deliveries",
+ "sync_error_generic": "Something went wrong. Try again later.",
+ "sync_complete": "Sync complete. {count} messages received.",
+ "sync_error": "Sync error: {status}",
+ "propagation_sync_live": "Syncing: {status} ({progress}%)",
+ "propagation_sync_state": {
+ "idle": "Idle",
+ "path_requested": "Requesting path",
+ "link_establishing": "Establishing link",
+ "link_established": "Link established",
+ "request_sent": "Request sent",
+ "receiving": "Receiving messages",
+ "response_received": "Processing response",
+ "complete": "Complete",
+ "no_path": "No path to node",
+ "link_failed": "Link failed",
+ "transfer_failed": "Transfer failed",
+ "no_identity_received": "No identity received",
+ "no_access": "Access denied",
+ "failed": "Failed",
+ "path_timeout": "Path timed out",
+ "unknown": "Unknown state"
+ },
+ "switching_identity": "Switching Identity...",
+ "loading_identity": "Loading your identity",
+ "loading_overlay_kicker": "One moment",
+ "loading_overlay_title": "Switching profile",
+ "loading_overlay_subtitle": "Loading your chats and settings. This usually takes a few seconds.",
+ "emergency_mode_active": "Emergency Mode Active - Using in-memory database and restricted services.",
+ "backend_disconnected": "Disconnected from backend",
+ "backend_process_stopped": "Reticulum backend stopped",
+ "backend_reconnected": "Reconnected to backend",
+ "restart_backend": "Restart backend",
+ "network_starting": "Connecting to the mesh…",
+ "network_degraded": "Mesh network unavailable. The app is still running so you can fix interfaces without wiping data.",
+ "recover_network": "Retry network",
+ "open_interfaces": "Open interfaces",
+ "network_recovered": "Network stack recovered",
+ "network_recover_failed": "Could not recover the network stack. Check interfaces and try again.",
+ "restart_backend_started": "Restarting backend…",
+ "restart_backend_failed": "Could not restart backend",
+ "memory_pressure_warning": "High memory use detected. Close large Nomad pages, voice notes, or downloads if you can, then dismiss this warning. Persistent pressure can come from hostile peer content.",
+ "view_backend_logs": "View crash log",
+ "view_backend_logs_failed": "No crash log available",
+ "blackhole_integration_enabled": "Blackhole Integration",
+ "blackhole_integration_description": "Automatically blackhole identities at the Reticulum transport layer when banishing users in MeshChatX.",
+ "announce_limits": "Announce Limits",
+ "announce_limits_description": "Limit how many announcements are kept in the database and how many are loaded at once. Clear a field and save to restore defaults.",
+ "announce_store_heading": "Store incoming announces",
+ "announce_store_description": "When off, received announces of this type are not saved. This only affects storage and does not stop the network or your own announces. Your local NomadNet node will still be listed.",
+ "announce_store_lxmf": "LXMF (lxmf.delivery)",
+ "announce_store_lxst": "LXST telephony (lxst.telephony)",
+ "announce_store_nomad": "NomadNet nodes (nomadnetwork.node)",
+ "announce_store_prop": "Propagation nodes (lxmf.propagation)",
+ "announce_max_stored_heading": "Max stored per aspect (database)",
+ "announce_fetch_limit_heading": "Default page size (API)",
+ "announce_search_max_fetch": "Max announces for search",
+ "announce_search_max_fetch_hint": "Maximum rows read from the database when you use text search on announces (per request).",
+ "discovered_interfaces_max_return": "Discovered interfaces cap",
+ "discovered_interfaces_max_return_hint": "Maximum discovered and active interface entries returned to the UI.",
+ "announce_limit_lxmf": "LXMF",
+ "announce_limit_nomadnet": "NomadNet",
+ "announce_limit_prop": "Prop Nodes",
+ "csp_settings": "Content Security Policy (CSP)",
+ "csp_description": "Manually add allowed domains for various resources. This is useful for custom map servers, bots, or external integrations.",
+ "csp_extra_connect_src": "Extra Connect Sources",
+ "csp_extra_connect_src_description": "Additional sources for fetch/websocket connections (e.g. https://api.example.com).",
+ "csp_extra_img_src": "Extra Image Sources",
+ "csp_extra_img_src_description": "Additional sources for images and tiles.",
+ "csp_extra_frame_src": "Extra Frame Sources",
+ "csp_extra_frame_src_description": "Additional sources for iframes.",
+ "csp_extra_script_src": "Extra Script Sources",
+ "csp_extra_script_src_description": "Additional sources for scripts (advanced use only).",
+ "csp_extra_style_src": "Extra Style Sources",
+ "csp_extra_style_src_description": "Additional sources for stylesheets.",
+ "location": "Location",
+ "location_source": "Location Source",
+ "location_source_disabled": "Disabled",
+ "location_source_disabled_desc": "Location sharing is turned off. Choose automatic or manual to share your coordinates.",
+ "location_source_browser": "Automatic (Browser)",
+ "location_source_manual": "Manual",
+ "location_source_browser_desc": "Uses your browser's geolocation API. Note: In the desktop app, this may use Google services.",
+ "location_source_manual_desc": "Use manually entered coordinates for maximum privacy.",
+ "location_manual_lat": "Latitude",
+ "location_manual_lon": "Longitude",
+ "location_manual_alt": "Altitude (m)",
+ "telemetry_enabled": "Telemetry Enabled",
+ "telemetry_description": "Allow MeshChatX to send and receive telemetry data, such as responding to location requests from trusted peers.",
+ "telemetry_trusted_peers": "Trusted Telemetry Peers",
+ "telemetry_no_trusted_peers": "No trusted peers configured. Trust peers from the chat menu to allow them to request your location.",
+ "telemetry_revoke_trust": "Revoke Trust",
+ "telemetry_trust_revoked": "Revoked telemetry trust for {name}",
+ "telemetry_trust_granted_alert": "Peer is now trusted for telemetry.",
+ "telemetry_trust_revoked_alert": "Telemetry trust revoked.",
+ "telemetry_trust_failed": "Failed to update telemetry trust.",
+ "telemetry_trust_revoke": "Revoke Telemetry Trust",
+ "telemetry_trust_grant": "Trust for Telemetry",
+ "location_manage_desc": "Manage how your location is shared.",
+ "map_settings_title": "Map",
+ "map_settings_desc": "Map defaults, tiles, offline mode, and remote overlay limits.",
+ "map_defaults_heading": "Map defaults",
+ "map_tiles_heading": "Tiles and offline",
+ "map_overlay_limits_heading": "Remote overlay limits",
+ "map_overlay_limits_desc": "Guards for NomadNet and RNGit KMZ/KML/GeoJSON imports. Values are clamped to safe ranges.",
+ "map_overlay_max_bytes": "Max overlay file size (bytes)",
+ "map_overlay_max_features": "Max features per overlay",
+ "map_overlay_max_kmz_uncompressed_bytes": "Max KMZ uncompressed size (bytes)",
+ "map_overlay_max_sources": "Max overlay sources",
+ "map_overlay_max_concurrent_jobs": "Max concurrent overlay jobs",
+ "map_overlay_path_timeout_seconds": "Path lookup timeout (seconds)",
+ "map_overlay_transfer_timeout_seconds": "Transfer timeout (seconds)",
+ "map_overlay_job_timeout_seconds": "Job timeout (seconds)",
+ "map_overlay_max_retries": "Max fetch retries",
+ "map_overlay_retry_delay_seconds": "Retry base delay (seconds)",
+ "map_default_lat": "Default latitude",
+ "map_default_lon": "Default longitude",
+ "map_default_zoom": "Default zoom",
+ "map_tile_server_url": "Tile server URL",
+ "map_nominatim_api_url": "Nominatim API URL",
+ "map_offline_enabled": "Offline MBTiles enabled",
+ "map_tile_cache_enabled": "Tile cache enabled",
+ "battery_unavailable": "Battery status unavailable",
+ "battery_level_title": "Battery {percent}",
+ "battery_charging_title": "Charging {percent}",
+ "battery_discharging_title": "On battery {percent}"
+ },
+ "common": {
+ "open": "Open",
+ "close": "Close",
+ "cancel": "Cancel",
+ "select": "Select",
+ "save": "Save",
+ "block": "Banish",
+ "delete": "Delete",
+ "edit": "Edit",
+ "add": "Add",
+ "sync": "Sync",
+ "restart_app": "Restart App",
+ "reveal": "Reveal",
+ "refresh": "Refresh",
+ "copy": "Copy",
+ "copy_to_clipboard": "Copy to clipboard",
+ "vacuum": "Vacuum",
+ "auto_recover": "Auto Recover",
+ "shutdown": "Shutdown",
+ "acknowledge_reset": "Acknowledge & Reset",
+ "continue": "Continue",
+ "confirm": "Confirm",
+ "delete_confirm": "Are you sure you want to delete this? This cannot be undone.",
+ "search": "Search tools...",
+ "no_results": "No tools found",
+ "error": "Error",
+ "success": "Success",
+ "warning": "Warning",
+ "info": "Info",
+ "copied": "Copied to clipboard",
+ "failed_to_copy": "Failed to copy to clipboard",
+ "save_failed": "Failed to save configuration!",
+ "invalid_address": "Invalid Address",
+ "loading": "Loading...",
+ "ok": "OK",
+ "clear": "Clear",
+ "prompt_title": "Enter a value"
+ },
+ "stickers": {
+ "settings_title": "Stickers",
+ "settings_description": "Your saved images for this identity. Use them in messages or save images from chats. You can also export or import a backup.",
+ "export": "Export stickers",
+ "import": "Import stickers",
+ "export_done": "Stickers exported",
+ "replace_duplicates": "Replace duplicate images when importing",
+ "count": "{count} saved sticker(s)",
+ "open_picker": "Stickers",
+ "picker_tooltip": "Emoji, stickers, and GIFs",
+ "tab_emojis": "Emojis",
+ "tab_stickers": "Stickers",
+ "empty_library": "No stickers yet.",
+ "drop_or_click_hint": "Drop images here or click to browse (PNG, JPEG, GIF, WebP, BMP, TGS, WebM, max 512 KB each).",
+ "add_more_hint": "Drop or click to add more stickers.",
+ "file_too_large": "Each sticker must fit within its format limit (TGS 64 KB, WebM 256 KB, image 512 KB).",
+ "unsupported_type": "Use PNG, JPEG, GIF, WebP, BMP, TGS, or WebM.",
+ "uploaded_count": "Added {count} sticker(s).",
+ "save_to_library": "Save image to stickers",
+ "saved": "Sticker saved",
+ "duplicate": "Already in your library",
+ "save_failed": "Could not save sticker",
+ "import_success": "Imported {imported}, skipped {skipped_duplicates} duplicates, invalid {skipped_invalid}",
+ "import_failed": "Failed to import stickers",
+ "all_stickers": "All stickers",
+ "unpacked": "Unpacked",
+ "open_editor": "Open sticker editor",
+ "upload_short": "Upload",
+ "format_static": "Static",
+ "format_animated": "Animated",
+ "format_video": "Video",
+ "format_legacy": "Legacy",
+ "errors": {
+ "file_too_large_static": "Static stickers must be 512 KB or smaller.",
+ "file_too_large_animated": "Animated TGS stickers must be 64 KB or smaller.",
+ "file_too_large_video": "Video WEBM stickers must be 256 KB or smaller.",
+ "invalid_dimensions": "Stickers must be at most 512x512 pixels with one side exactly 512.",
+ "invalid_duration": "Animated and video stickers must be 3 seconds or shorter.",
+ "invalid_fps": "Animated stickers must be 30-60 FPS, video stickers up to 30 FPS.",
+ "invalid_codec": "Video stickers must use the VP9 codec.",
+ "invalid_audio": "Video stickers must not contain an audio stream.",
+ "invalid_tgs": "Could not read TGS animation.",
+ "invalid_webm": "Could not read WebM video."
+ },
+ "packs": {
+ "title": "Sticker packs",
+ "description": "Group stickers into packs you can share or import.",
+ "create": "New pack",
+ "create_pack": "Create sticker pack",
+ "title_label": "Title",
+ "title_placeholder": "My pack",
+ "short_name_label": "Short name",
+ "short_name_placeholder": "e.g. cats",
+ "description_label": "Description",
+ "type_label": "Type",
+ "type_static": "Static",
+ "type_animated": "Animated",
+ "type_video": "Video",
+ "type_mixed": "Mixed",
+ "save": "Save",
+ "cancel": "Cancel",
+ "open_editor": "Add stickers",
+ "export": "Export pack",
+ "import": "Import pack",
+ "delete": "Delete pack",
+ "delete_confirm": "Delete this pack?",
+ "delete_with_stickers": "Also delete stickers in this pack",
+ "deleted": "Pack deleted",
+ "created": "Pack created",
+ "exported": "Pack exported",
+ "installed": "Pack installed: imported {imported}, skipped {skipped}",
+ "install_failed": "Failed to install pack",
+ "empty": "No packs yet.",
+ "count_one": "{count} sticker",
+ "count_other": "{count} stickers",
+ "stickers_in_pack": "Stickers in {title}",
+ "no_stickers": "No stickers in this pack yet."
+ },
+ "editor": {
+ "title": "Sticker editor",
+ "open": "Open editor",
+ "select_image": "Select image",
+ "drop_image": "Drop an image, or click to browse",
+ "reset": "Reset",
+ "remove_background": "Remove background",
+ "removing_background": "Removing background...",
+ "white_stroke": "White stroke",
+ "shadow": "Drop shadow",
+ "stroke_size": "Stroke size",
+ "scale": "Scale",
+ "rotate": "Rotate",
+ "offset_x": "Offset X",
+ "offset_y": "Offset Y",
+ "text_overlay": "Text overlay",
+ "text_placeholder": "Add text...",
+ "text_color": "Text color",
+ "text_size": "Text size",
+ "format": "Export format",
+ "format_png": "PNG",
+ "format_webp": "WebP",
+ "quality": "Quality",
+ "emoji": "Emoji",
+ "emoji_placeholder": "Optional",
+ "pack": "Pack",
+ "no_pack": "No pack",
+ "save": "Save sticker",
+ "saved": "Sticker saved",
+ "save_failed": "Could not save sticker",
+ "current_size": "Current size: {size}",
+ "size_too_large": "Result is larger than 512 KB. Try lowering quality or using PNG with simpler shapes."
+ }
+ },
+ "sticker_editor": {
+ "title": "Sticker editor",
+ "create_button": "Create sticker",
+ "processing": "Processing...",
+ "canvas_hint": "Preview is scaled to fit. Export uses a 512×512 canvas.",
+ "section_source": "Source",
+ "choose_image": "Choose image",
+ "section_transform": "Transform",
+ "scale": "Scale",
+ "rotation": "Rotation",
+ "flip_h": "Flip horizontal",
+ "flip_v": "Flip vertical",
+ "reset_transform": "Reset transform",
+ "section_effects": "Effects",
+ "bg_removed": "Background removed",
+ "remove_background": "Remove background",
+ "white_stroke": "White stroke",
+ "shadow": "Drop shadow",
+ "section_overlay": "Text overlay",
+ "overlay_placeholder": "Add text...",
+ "font_size": "Font size",
+ "overlay_y": "Vertical position",
+ "text_color": "Text color",
+ "stroke_color": "Stroke color",
+ "section_meta": "Details",
+ "name_placeholder": "Sticker name",
+ "emoji_placeholder": "Emoji (optional)",
+ "format": "Export format",
+ "quality": "Quality",
+ "size_label": "Size: {size} (max {limit})",
+ "cancel": "Cancel",
+ "save": "Save sticker",
+ "image_load_failed": "Could not load image",
+ "removing_background": "Removing background...",
+ "bg_removal_failed": "Background removal failed",
+ "too_large": "Exported file is larger than 512 KB",
+ "saving": "Saving...",
+ "saved": "Sticker saved",
+ "save_failed": "Could not save sticker"
+ },
+ "sticker_packs": {
+ "all": "All",
+ "section_title": "Sticker packs",
+ "section_description": "Group stickers into packs you can share or import.",
+ "create": "New pack",
+ "open_editor": "Add stickers",
+ "install_from_file": "Install from file",
+ "empty": "No packs yet.",
+ "count_label": "{count} stickers",
+ "add_sticker": "Add sticker",
+ "export": "Export pack",
+ "delete": "Delete pack",
+ "empty_pack": "No stickers in this pack yet.",
+ "create_title": "Create sticker pack",
+ "field_title": "Title",
+ "field_short_name": "Short name",
+ "field_description": "Description",
+ "type_static": "Static",
+ "type_animated": "Animated",
+ "type_video": "Video",
+ "type_mixed": "Mixed",
+ "strict_label": "Enforce pack type (reject mismatched uploads)",
+ "created": "Pack created",
+ "create_failed": "Could not create pack",
+ "exported": "Pack exported",
+ "export_failed": "Export failed",
+ "confirm_delete": "Delete pack \"{title}\"?",
+ "deleted": "Pack deleted",
+ "delete_failed": "Could not delete pack",
+ "installed": "Pack installed ({imported} imported)",
+ "install_failed": "Could not install pack"
+ },
+ "gifs": {
+ "settings_title": "GIFs",
+ "settings_description": "Your personal GIF library. Upload GIF and animated WebP files to reuse them in chats. Your most-used GIFs are shown first.",
+ "export": "Export GIFs",
+ "import": "Import GIFs",
+ "export_done": "GIFs exported",
+ "replace_duplicates": "Replace duplicate GIFs when importing",
+ "count": "{count} saved GIF(s)",
+ "tab_gifs": "GIFs",
+ "empty_library": "No GIFs yet. Upload a GIF or save one from a chat.",
+ "drop_or_click_hint": "Drop GIFs here or click to browse (GIF or WebP, max 5 MB each).",
+ "add_more_hint": "Drop or click to add more GIFs.",
+ "file_too_large": "Each GIF must be 5 MB or smaller.",
+ "unsupported_type": "Use GIF or WebP files only.",
+ "uploaded_count": "Added {count} GIF(s).",
+ "save_to_library": "Save image to GIFs",
+ "saved": "GIF saved",
+ "duplicate": "Already in your GIF library",
+ "save_failed": "Could not save GIF",
+ "import_success": "Imported {imported}, skipped {skipped_duplicates} duplicates, invalid {skipped_invalid}",
+ "import_failed": "Failed to import GIFs"
+ },
+ "plugins": {
+ "settings": {
+ "title": "Plugins",
+ "description": "Install, enable, and review permissions for MeshChatX plugins. ZIP and WASM installs show a confirmation dialog with permissions, signature status, and security findings.",
+ "enable": "Enable",
+ "disable": "Disable",
+ "remove": "Remove",
+ "permissions": "Permissions",
+ "install_zip": "Install plugin from ZIP or WASM bundle",
+ "enabled": "Plugin enabled",
+ "disabled": "Plugin disabled",
+ "removed": "Plugin removed",
+ "installed": "Plugin installed",
+ "auto_disabled": "Plugin auto-disabled: {reason}",
+ "kill_switch": "A plugin was disabled: {reason}",
+ "empty_state": "No plugins installed yet. Upload a plugin ZIP or WASM bundle to get started.",
+ "drag_drop": "Drag and drop a plugin ZIP or WASM bundle here",
+ "choose_file": "Choose ZIP or WASM file",
+ "confirm_remove": "Remove plugin \"{name}\"? This deletes its files and stored data.",
+ "badge_enabled": "Enabled",
+ "badge_disabled": "Disabled",
+ "badge_frontend": "UI",
+ "badge_wasm": "WASM",
+ "badge_python": "Python",
+ "badge_signed": "Signed",
+ "badge_trusted": "Trusted",
+ "badge_invalid_signature": "Invalid signature",
+ "badge_tampered": "Tampered",
+ "install_failed": "Plugin install failed: {reason}",
+ "installing": "Installing plugin...",
+ "badge_network": "Network",
+ "network_endpoints": "Network endpoints",
+ "previewing": "Reviewing plugin..."
+ },
+ "permissions": {
+ "none": "No elevated permissions",
+ "hooks.announce.received": "Receive mesh announce events",
+ "hooks.rns.link.event": "Receive RNS link packet and close events",
+ "managers.destinationPath.read": "Read the Reticulum path table",
+ "managers.debugLog.read": "Read application debug logs",
+ "managers.bugReport.status": "Read bug report collector status",
+ "managers.bugReport.listCollectors": "List heard mcx-bugs-v1 collectors",
+ "managers.bugReport.listReports": "List received bug reports",
+ "managers.bugReport.preview": "Preview redacted debug logs for bug reports",
+ "managers.bugReport.send": "Send bug reports over the mesh",
+ "managers.bugReport.startCollector": "Start an mcx-bugs-v1 collector",
+ "managers.bugReport.stopCollector": "Stop the bug report collector",
+ "managers.bugReport.announce": "Announce the bug report collector",
+ "managers.rnsLink.open": "Open RNS links to destinations",
+ "managers.rnsLink.identify": "Identify on RNS links",
+ "managers.rnsLink.request": "Send request/response over RNS links",
+ "managers.rnsLink.send": "Send packets over RNS links",
+ "managers.rnsLink.close": "Close RNS links",
+ "storage.isolated": "Use isolated plugin storage",
+ "network.fetch": "Make outbound internet HTTP requests"
+ },
+ "install_dialog": {
+ "title": "Install plugin",
+ "network_title": "Install plugin with network access",
+ "message": "Review permissions for \"{name}\" ({id}) before installing.",
+ "permissions": "Requested permissions",
+ "permissions_hint": "Uncheck any permission you do not want to grant. Denied capabilities stay unavailable at runtime.",
+ "grant": "Allow",
+ "network_endpoints": "External network endpoints",
+ "network_endpoints_blocked": "Network access is not granted. These URLs will remain blocked.",
+ "network_endpoints_unknown": "This plugin requests network access but did not declare specific endpoints.",
+ "signature": "Signature",
+ "signature_unsigned": "Unsigned",
+ "signature_signed": "Signed",
+ "signature_trusted": "Trusted publisher",
+ "signature_invalid": "Invalid signature (install blocked)",
+ "trust_publisher": "Trust this publisher",
+ "security_findings": "Security findings",
+ "cancel": "Cancel",
+ "confirm": "Install",
+ "close": "Close"
+ },
+ "sideband": {
+ "title": "Sideband plugins",
+ "description": "Optional Sideband-compatible Python plugins loaded from a directory. They run in-process with full host access when enabled.",
+ "master_enable": "Enable Sideband plugin loader (dangerous)",
+ "command_enable": "Enable Sideband command plugins",
+ "path": "Plugin directory path",
+ "browse": "Browse",
+ "browse_title": "Choose Sideband plugins folder",
+ "path_prompt": "Enter the full path to the Sideband plugins folder",
+ "path_picked": "Plugin folder selected",
+ "save": "Save Sideband settings",
+ "reload": "Reload Sideband plugins",
+ "loaded": "Loaded Sideband plugins",
+ "saved": "Sideband plugin settings saved",
+ "reloaded": "Sideband plugins reloaded",
+ "danger_confirm": "Sideband plugins run as full Python code inside MeshChatX with filesystem, network, and LXMF access. Enable only if you trust every script in the directory."
+ }
+ },
+ "selftest": {
+ "title": "System Self-Test",
+ "description": "Run diagnostic checks for the network stack, database, identity, imports, storage, LXMF, subprocess/run-module, SQLite, identity files, loopback TCP, unicode paths, RNode helpers, bot launcher argv, HTTP status/config/health/auth/bots/security/interfaces/reticulum-instance/identities/favourites/telephone/plugins/sideband/RRC APIs, plugin runtime, WebSocket, RNS Link API, and bot lifecycle.",
+ "run_test_btn": "Run Diagnostics",
+ "running": "Running Diagnostics...",
+ "stack_up": "Network Stack",
+ "config_good": "Configuration Integrity",
+ "db_good": "Database Connection",
+ "read_write": "Storage Read/Write",
+ "identity_good": "Identity Loaded",
+ "imports_good": "Critical Imports",
+ "storage_lock_good": "Storage Lock",
+ "temp_fs_good": "Temp Filesystem",
+ "public_assets_good": "Public Assets",
+ "lxmf_router_good": "LXMF Router",
+ "subprocess_good": "Subprocess Spawn",
+ "run_module_good": "MeshChatX Run-Module",
+ "sqlite_roundtrip": "SQLite Roundtrip",
+ "identity_roundtrip": "Identity File Roundtrip",
+ "loopback_tcp": "Loopback TCP Bind",
+ "unicode_path_good": "Unicode Path I/O",
+ "rnode_support_good": "RNode Support Module",
+ "bot_launcher_good": "Bot Launcher Argv",
+ "http_status_good": "HTTP Status",
+ "http_app_info_good": "HTTP App Info",
+ "http_config_good": "HTTP Config",
+ "http_db_health_good": "HTTP Database Health",
+ "http_auth_csrf_good": "HTTP Auth CSRF",
+ "http_bots_status_good": "HTTP Bots Status",
+ "http_security_good": "HTTP Server Security",
+ "http_interfaces_good": "HTTP RNS Interfaces",
+ "http_reticulum_instance_good": "HTTP RNS Instance Settings",
+ "http_identities_good": "HTTP Identities",
+ "http_favourites_good": "HTTP Favourites",
+ "http_telephone_good": "HTTP Telephone Status",
+ "http_plugins_good": "HTTP Plugins API",
+ "http_plugins_trust_good": "HTTP Plugin Trust List",
+ "http_sideband_plugins_good": "HTTP Sideband Plugins",
+ "http_sideband_config_good": "HTTP Sideband Config",
+ "http_rrc_hubs_good": "HTTP RRC Hubs",
+ "http_rrc_servers_good": "HTTP RRC Servers",
+ "plugins_runtime_good": "Plugin Manager Runtime",
+ "websocket_good": "WebSocket",
+ "websocket_rns_link_good": "WebSocket RNS Link API",
+ "bots_lifecycle": "Bot Create / Start / Stop / Delete",
+ "passed": "Passed",
+ "failed": "Failed",
+ "expand_reason": "Show failure details",
+ "collapse_reason": "Hide failure details",
+ "status_label": "Status",
+ "reason_label": "Reason",
+ "checks_completed": "All checks passed successfully."
+ },
+ "maintenance": {
+ "title": "Maintenance & Data",
+ "description": "Manage your local data, clear caches, and backup conversations.",
+ "purge_old_title": "Clean up old messages",
+ "purge_old_desc": "Delete local messages older than a number of days or before a date. Attachments stored in those messages are removed too. Export an archive first if you may want to reimport later.",
+ "purge_mode_days": "Older than days",
+ "purge_mode_date": "Before date",
+ "purge_older_than_days": "Delete messages older than (days)",
+ "purge_before_date": "Delete messages before",
+ "purge_preview": "Count matching",
+ "purge_preview_loading": "Counting matching messages...",
+ "purge_preview_hint": "Choose a filter, then count matching messages.",
+ "purge_preview_count": "{count} messages match this filter",
+ "purge_filter_invalid": "Enter a valid day count or date.",
+ "export_old_archive": "Export matching archive",
+ "export_old_archive_done": "Matching message archive downloaded",
+ "purge_old_confirm_btn": "Delete matching",
+ "purge_old_confirm": "Permanently delete matching local messages and their attachments? This cannot be undone.",
+ "purge_old_done": "Deleted {count} messages",
+ "clear_messages": "Clear All Messages",
+ "clear_messages_desc": "Permanently delete all sent and received messages.",
+ "clear_duplicates": "Clear Duplicate Messages",
+ "clear_duplicates_desc": "Remove extra copies that share the same text in the same conversation. Keeps the oldest of each set.",
+ "clear_duplicates_confirm": "Delete duplicate messages that match by content in each conversation? The oldest copy of each set is kept. This cannot be undone.",
+ "clear_duplicates_done": "Removed {count} duplicate messages",
+ "clear_announces": "Clear All Announces",
+ "clear_announces_desc": "Remove all discovered peers and nodes from your cache.",
+ "clear_nomadnet_favs": "Clear NomadNetwork Favorites",
+ "clear_nomadnet_favs_desc": "Remove all saved NomadNetwork nodes from your favorites.",
+ "clear_lxmf_icons": "Clear LXMF Icons",
+ "clear_lxmf_icons_desc": "Remove cached LXMF user icons from your device.",
+ "clear_stickers": "Clear All Stickers",
+ "clear_stickers_desc": "Remove every saved sticker image for this identity.",
+ "stickers_cleared": "Stickers cleared",
+ "clear_gifs": "Clear All GIFs",
+ "clear_gifs_desc": "Remove every saved GIF for this identity.",
+ "gifs_cleared": "GIFs cleared",
+ "clear_archives": "Clear Page Archives",
+ "clear_archives_desc": "Delete all cached NomadNetwork pages.",
+ "clear_reticulum_docs": "Clear Reticulum Docs",
+ "clear_reticulum_docs_desc": "Removes all downloaded Reticulum documentation and versions.",
+ "clear_path_table": "Clear Path Table",
+ "clear_path_table_desc": "Drop all cached Reticulum transport paths. Routes will be re-discovered as needed.",
+ "path_table_cleared": "Path table cleared",
+ "export_messages": "Export Messages",
+ "export_messages_desc": "Download a reimportable JSON archive (messages, attachments, contacts, read state).",
+ "export_messages_done": "Message archive downloaded",
+ "import_messages": "Import Messages",
+ "import_messages_desc": "Restore conversations from a previously exported JSON archive.",
+ "clear_confirm": "Are you sure? This action cannot be undone.",
+ "messages_cleared": "Messages cleared successfully",
+ "announces_cleared": "Announces cleared successfully",
+ "favourites_cleared": "Favorites cleared successfully",
+ "lxmf_icons_cleared": "LXMF icons cleared successfully",
+ "archives_cleared": "Archives cleared successfully",
+ "docs_cleared": "Reticulum documentation cleared successfully",
+ "import_success": "Successfully imported {count} messages",
+ "import_failed": "Failed to import messages",
+ "export_nomadnet_favourites": "Export NomadNet favourites",
+ "import_nomadnet_favourites": "Import NomadNet favourites",
+ "nomadnet_favourites_exported": "NomadNet favourites layout exported",
+ "nomadnet_favourites_export_failed": "Could not export NomadNet favourites",
+ "nomadnet_favourites_imported": "NomadNet favourites layout imported",
+ "nomadnet_favourites_import_failed": "Could not import NomadNet favourites file"
+ },
+ "identities": {
+ "title": "Identities",
+ "eyebrow": "Account",
+ "manage": "Use different names on the same device. Switch anytime without reinstalling.",
+ "active_identity": "Active identity",
+ "other_identities": "Other identities",
+ "show_addresses": "Show addresses",
+ "hide_addresses": "Hide addresses",
+ "lxst_address": "LXST address",
+ "new_identity": "New Identity",
+ "import": "Import",
+ "import_hint": "Restore from a backup file or pasted backup text.",
+ "import_key_only_hint": "This restores the identity key only. Message history and settings require a database backup zip from About.",
+ "export_all": "Backup all",
+ "export_all_success": "All identities backed up",
+ "export_all_failed": "Failed to back up identities",
+ "generate_fresh": "Create a new name and key pair for the mesh.",
+ "display_name": "Display Name",
+ "display_name_hint": "e.g. Work, Field radio, Personal",
+ "current": "Active",
+ "switch": "Switch to this identity",
+ "switch_label": "Switch",
+ "delete": "Delete identity",
+ "no_identities": "No identities yet",
+ "create_first": "Create an identity to start chatting on the mesh.",
+ "switch_confirm": "Switch to \"{name}\"?",
+ "switch_after_restore_confirm": "Switch to restored identity \"{name}\" now?",
+ "delete_confirm": "Delete \"{name}\"? This cannot be undone.",
+ "switched": "Identity switched successfully.",
+ "created": "Identity created successfully",
+ "deleted": "Identity deleted",
+ "failed_load": "Failed to load identities",
+ "enter_display_name_warning": "Please enter a display name",
+ "failed_create": "Failed to create identity",
+ "switch_scheduled": "Switch scheduled. Reloading application...",
+ "failed_switch": "Failed to switch identity",
+ "failed_delete": "Failed to delete identity",
+ "key_control": "Backup",
+ "export_key_file": "Export backup file",
+ "copy_base32": "Copy backup text",
+ "upload_key_file": "Upload backup file",
+ "paste_base32": "Paste backup text",
+ "paste_base32_placeholder": "Paste your identity backup text here...",
+ "confirm_restore": "Confirm restore",
+ "restoring": "Restoring...",
+ "identity_exported": "Backup file exported",
+ "identity_copied": "Backup text copied to clipboard",
+ "identity_export_failed": "Failed to export backup",
+ "identity_copy_failed": "Failed to copy backup",
+ "identity_restored": "Identity restored.",
+ "identity_restore_failed": "Identity restore failed",
+ "identity_restore_empty_file": "Identity file is empty.",
+ "identity_restore_file_too_large": "Identity file is too large.",
+ "no_identity_available": "No identity available",
+ "message_count": "{count} messages"
+ },
+ "about": {
+ "title": "About",
+ "third_party_licenses": "Third-party licenses",
+ "version": "v{version}",
+ "ui_build": "UI build {date}",
+ "rns_version": "RNS {version}",
+ "lxmf_version": "LXMF {version}",
+ "python_version": "Python {version}",
+ "config_path": "Config path",
+ "database_path": "Database path",
+ "database_size": "Database size",
+ "active_sessions": "Active sessions",
+ "active_sessions_description": "Browsers and devices currently connected to this MeshChatX instance over the UI WebSocket.",
+ "active_sessions_empty": "No active sessions.",
+ "active_sessions_count": "{count} active",
+ "active_session_ip": "IP address",
+ "active_session_user_agent": "User agent",
+ "active_session_connected": "Connected",
+ "database_health": "Database Health",
+ "database_health_description": "Quick check, WAL tuning, and recovery tools for the MeshChatX database.",
+ "running_checks": "Running checks...",
+ "integrity": "Integrity",
+ "journal_mode": "Journal mode",
+ "wal_autocheckpoint": "WAL autocheckpoint",
+ "page_size": "Page size",
+ "pages_free": "Pages / Free",
+ "free_space_estimate": "Free space estimate",
+ "system_resources": "System Resources",
+ "live": "Live",
+ "memory_rss": "Memory (RSS)",
+ "virtual_memory": "Virtual Memory",
+ "network_stats": "Network Stats",
+ "sent": "Sent",
+ "received": "Received",
+ "packets_sent": "Packets Sent",
+ "packets_received": "Packets Received",
+ "reticulum_stats": "Reticulum Stats",
+ "total_paths": "Total Paths",
+ "announces_per_second": "Announces / sec",
+ "announces_per_minute": "Announces / min",
+ "announces_per_hour": "Announces / hr",
+ "download_activity": "Download Activity",
+ "no_downloads_yet": "No downloads yet",
+ "runtime_status": "Runtime Status",
+ "shared_instance": "Shared Instance",
+ "standalone_instance": "Standalone Instance",
+ "transport_enabled": "Transport Enabled",
+ "transport_disabled": "Transport Disabled",
+ "identity_addresses": "Identity & Addresses",
+ "telephone_address": "Telephone Address",
+ "security_integrity": "Integrity Monitor",
+ "security_integrity_description": "Advisory tamper-evidence. Detects unexpected changes to your identity and database files between startups. It is not tamper-proofing and does not protect against an attacker with access to this account.",
+ "secured": "No changes detected",
+ "tampering_detected": "Changes detected",
+ "technical_issues": "Technical Issues:",
+ "integrity_backend_error": "The application backend binary (unpacked from ASAR) appears to have changed since the last snapshot. If you did not update or modify it, review the change.",
+ "integrity_data_error": "Your identity or database files appear to have changed while the app was closed.",
+ "integrity_warning_footer": "This is advisory, not a confirmed compromise. If you updated the app or edited these files yourself, you can acknowledge to reset the baseline.",
+ "no_integrity_violations": "No unexpected changes to monitored files since last startup.",
+ "dependency_chain": "Stack versions",
+ "app_name": "MeshChatX",
+ "automatic_backups_desc": "Automated daily snapshots of your database.",
+ "automatic_backups_title": "Automatic Backups",
+ "backup_download_failed": "Failed to download backup",
+ "backup_downloaded": "Backup downloaded",
+ "backend_stack": "Python packages",
+ "chrome_runtime": "Chrome",
+ "contact_alternate": "Alternate",
+ "contact_details": "Details",
+ "contact_developer": "Contact Developer",
+ "contact_support_title": "Contact & support",
+ "contact_lxmf_address": "LXMF Address",
+ "contact_propagation_hint": "Send to propagation node if you cant reach me!",
+ "contact_open_messages": "Open conversation",
+ "contact_copy_address": "Copy address",
+ "donate_label": "Donate",
+ "donate_monero_label": "Monero (XMR)",
+ "donate_copy_monero": "Copy Monero address",
+ "donate_kofi": "Ko-fi",
+ "donate_buymeacoffee": "Buy Me a Coffee",
+ "copied_label_to_clipboard": "{label} copied to clipboard",
+ "core_runtime": "Runtime versions",
+ "creating": "Creating...",
+ "database_backups_desc": "Full snapshots of your communications database.",
+ "database_backups_title": "Database Backups",
+ "database_health_maintenance": "Database Health & Maintenance",
+ "dep_lxmf_subtitle": "LXMF",
+ "dep_lxmfy_subtitle": "LXMFy",
+ "dep_rns_subtitle": "RNS",
+ "download_backup": "Download Backup",
+ "downloading": "Downloading...",
+ "electron_runtime": "Electron",
+ "environment_information": "Environment Information",
+ "env_backend_url": "Backend URL",
+ "env_language": "Language",
+ "env_lxmf": "LXMF",
+ "env_platform": "Platform",
+ "env_python": "Python",
+ "env_rns": "RNS",
+ "env_user_agent": "User Agent",
+ "failed_to_copy_label": "Failed to copy {label}",
+ "free_space": "Free Space",
+ "identity_hash": "Identity Hash",
+ "journal_short": "Journal",
+ "local_snapshots_desc": "Create point-in-time restore points on disk.",
+ "local_snapshots_title": "Local Snapshots",
+ "lxmf_address": "LXMF Address",
+ "lxst_engine": "LXST",
+ "main_instance_badge": "Main Instance",
+ "nodejs_runtime": "Node.js",
+ "page_count_label": "Page Count",
+ "pagination_page_of": "Page {current} of {total}",
+ "path_unknown": "unknown",
+ "recovery": "Recovery",
+ "recovery_complete": "Database recovery finished.",
+ "recovery_confirm": "Run database recovery? This runs a full integrity check and compacts the database (VACUUM). It can take a while and the app may feel unresponsive. This does not load a backup file.",
+ "recovery_failed": "Recovery failed",
+ "vacuum_complete": "Database vacuum finished.",
+ "vacuum_failed": "Could not vacuum the database.",
+ "reveal_config_file": "Reveal File",
+ "reveal_database_file": "Reveal DB",
+ "reticulum_config": "Reticulum Config",
+ "shared_instance_badge": "Shared Instance: {address}",
+ "shutdown_confirm": "Are you sure you want to shutdown the app? This will stop the server and close the application.",
+ "snapshot_create": "Create",
+ "snapshot_download": "Download",
+ "snapshot_download_failed": "Failed to download snapshot",
+ "snapshot_downloaded": "Snapshot downloaded",
+ "snapshot_placeholder": "Snapshot label...",
+ "snapshot_restore": "Restore",
+ "tagline_after": ".",
+ "tagline_lead": "A secure, resilient, and beautiful communications platform powered by the",
+ "tagline_link": "Reticulum Network Stack",
+ "technical_issues_detected": "Changes since last snapshot",
+ "other_core_components": "Other Core Components",
+ "backend_dependencies": "Backend Dependencies",
+ "delete_snapshot_confirm": "Are you sure you want to delete this snapshot?",
+ "snapshot_deleted": "Snapshot deleted",
+ "failed_delete_snapshot": "Failed to delete snapshot",
+ "delete_backup_confirm": "Are you sure you want to delete this backup?",
+ "backup_deleted": "Backup deleted",
+ "failed_delete_backup": "Failed to delete backup",
+ "database_restored": "Database restored. Relaunching...",
+ "failed_restore_snapshot": "Failed to restore snapshot",
+ "restore_snapshot_confirm": "Are you sure you want to restore this snapshot? This will overwrite the current database and require an app relaunch.",
+ "restore_from_file": "Restore from File",
+ "restoring": "Restoring...",
+ "restore_select_file": "Select a backup file to restore.",
+ "restore_file_confirm": "Are you sure you want to restore from this backup file? This will overwrite the current database and require an app relaunch.",
+ "failed_restore_file": "Failed to restore from file",
+ "integrity_acknowledged": "Integrity issues acknowledged",
+ "integrity_acknowledged_reset": "Integrity issues acknowledged and manifest reset",
+ "integrity_acknowledge_confirm": "Are you sure you want to acknowledge these integrity issues? This will update the security manifest to match the current state of your files.",
+ "failed_acknowledge_integrity": "Failed to acknowledge integrity issues",
+ "shutdown_sent": "Shutdown command sent to server.",
+ "identity_exported": "Identity key file exported",
+ "identity_copied": "Identity Base32 key copied to clipboard",
+ "env_battery": "Battery",
+ "env_battery_unavailable": "Unavailable on this device",
+ "env_battery_charging": "Charging {percent}",
+ "env_battery_on_battery": "On battery {percent}",
+ "usage_insights": "MeshChatX usage",
+ "process_cpu": "Process CPU",
+ "process_threads": "Threads",
+ "process_uptime": "Uptime",
+ "memory_pressure": "Memory pressure",
+ "memory_pressure_relaxed": "Active (low host RAM)",
+ "memory_pressure_normal": "Normal",
+ "memory_pressure_paths": "{count} paths tracked",
+ "path_table": "Path table",
+ "path_table_count": "{count} paths",
+ "env_host_battery": "Host battery",
+ "app_battery_use": "Est. MeshChatX battery",
+ "app_battery_use_hint": "Estimated from MeshChatX CPU time since start. Not an OS battery attribution.",
+ "app_battery_use_warming": "Measuring…",
+ "app_battery_use_rate": "~{rate}",
+ "app_battery_use_with_intensity": "~{rate} ({intensity})",
+ "app_battery_share": "Est. CPU share",
+ "app_battery_share_value": "{percent}% of device",
+ "app_battery_intensity_low": "low",
+ "app_battery_intensity_moderate": "moderate",
+ "app_battery_intensity_high": "high",
+ "app_battery_intensity_very_high": "very high",
+ "battery_saver": "Battery saver",
+ "battery_saver_off": "Off",
+ "battery_saver_on": "On",
+ "battery_saver_measures": "Active measures",
+ "battery_saver_measure": {
+ "disableVisualiserDiscovery": "Visualiser discovery hidden",
+ "hideOfflineInterfaces": "Offline interfaces hidden",
+ "maxVisualiserInterfaces": "Visualiser interface limit",
+ "disableVisualiserAutoReload": "Visualiser auto-reload off",
+ "slowVisualiserReload": "Slower visualiser reload",
+ "disableVisualiserLiveLayout": "Live layout off",
+ "reduceBackgroundPolling": "Slower background polling",
+ "reduceInterfacesDiscovery": "Slower interface discovery polls",
+ "applyInterfaceBitrateLimits": "Forced interface bitrates"
+ },
+ "top_memory_consumer": "Top memory",
+ "top_cpu_consumer": "Top CPU"
+ },
+ "interfaces": {
+ "title": "Interfaces",
+ "manage": "Manage",
+ "description": "Search, filter and export your Reticulum adapters.",
+ "add_interface": "Add Interface",
+ "edit_interface": "Edit Interface",
+ "import": "Import",
+ "export_all": "Export all",
+ "search_placeholder": "Search by name, type, host...",
+ "all": "All",
+ "connected_only": "Connected only",
+ "all_types": "All types",
+ "no_interfaces_found": "No interfaces found",
+ "no_interfaces_description": "Adjust your search or add a new interface.",
+ "fixed_mtu_hint": "Optional. If set, must be at least {min} bytes (Reticulum minimum MTU).",
+ "fixed_mtu_min": "Fixed MTU must be at least {min} bytes (Reticulum minimum).",
+ "restart_required": "Restart required",
+ "restart_description": "Reticulum MeshChatX must be restarted for any interface changes to take effect.",
+ "restart_now": "Restart now",
+ "failed_enable": "failed to enable interface",
+ "failed_disable": "failed to disable interface",
+ "delete_confirm": "Are you sure you want to delete this interface? This can not be undone!",
+ "failed_delete": "failed to delete interface",
+ "failed_export_all": "Failed to export interfaces",
+ "failed_export_single": "Failed to export interface",
+ "failed_reload": "Failed to reload Reticulum!",
+ "interface_not_found": "The selected interface for editing could not be found.",
+ "discovery_settings_saved": "Discovery settings saved",
+ "discovery_list_refreshed": "Discovered interfaces refreshed.",
+ "discovery_list_refresh_failed": "Could not refresh discovered interfaces.",
+ "discovery_default_bootstrap_only": "Default bootstrap-only for new outbound TCP",
+ "discovery_default_bootstrap_only_hint": "When enabled, new TCP client (and backbone connector) interfaces get bootstrap_only so Reticulum can detach them once autoconnect_discovered_interfaces is satisfied.",
+ "discovery_default_bootstrap_docs_link": "See Reticulum bootstrapping connectivity.",
+ "backbone_transport_identity_label": "Transport identity (optional)",
+ "backbone_transport_identity_placeholder": "e53433e51cde34c42a3245ba3fe1ad69",
+ "backbone_transport_identity_hint": "Matches Reticulum backbone remote examples that only set remote and target_port. Use the hash when an operator or directory specifies it.",
+ "mode_default_full": "Default (Full)",
+ "mode_full": "Full",
+ "mode_gateway": "Gateway",
+ "mode_access_point": "Access Point",
+ "mode_roaming": "Roaming",
+ "mode_boundary": "Boundary",
+ "mode_internal": "Internal",
+ "recursive_prs_label": "Recursive path requests",
+ "recursive_prs_hint": "Allow recursive path discovery on this interface regardless of mode (RNS recursive_prs).",
+ "announces_from_internal_label": "Propagate announces from internal interfaces",
+ "announces_from_internal_hint": "When off, announces received on internal-mode interfaces are not rebroadcast on this interface (RNS announces_from_internal).",
+ "location_cmd_label": "Location command (optional)",
+ "location_cmd_placeholder": "/path/to/gps-script",
+ "location_cmd_hint": "Absolute path to an executable that prints latitude,longitude,height. Reticulum runs it for discovery announces (RNS location_cmd).",
+ "block_fast_flapping_label": "Block fast-flapping clients",
+ "block_fast_flapping_hint": "Ignore clients that connect and disconnect too quickly (RNS 1.4.0 BackboneInterface defaults).",
+ "fast_flapping_block_time_label": "Block time (minutes)",
+ "fast_flapping_threshold_label": "Threshold (seconds)",
+ "fast_flapping_grace_label": "Grace flaps",
+ "loopback_local_title": "Local / loopback",
+ "loopback_local_body": "Reticulum uses a shared instance internally. To talk to another RNS process on this host, use a TCP Client to 127.0.0.1 with the instance port, or use an external interface module in the interface path.",
+ "loopback_local_docs_hint": "See the Interfaces chapter for supported types.",
+ "loopback_local_docs_link": "Open bundled Reticulum manual",
+ "custom_external_intro": "Types such as WeaveInterface or your own class are loaded when Reticulum finds a matching module file (TypeName.py) under interfacepath and exposes interface_class (see RNS source). Options below are merged into the interface stanza in your config file.",
+ "custom_external_install_intro": "On Android, Android/data is often hidden from the Files app. Use Install module to copy a TypeName.py into this app interfacepath via the system file picker (Downloads, Drive, Bluetooth, etc.).",
+ "custom_external_interfacepath_label": "Active interfacepath",
+ "custom_external_install_button": "Install module (.py)",
+ "custom_external_install_overwrite": "Overwrite if present",
+ "custom_external_install_success": "Interface module installed",
+ "custom_external_install_failed": "Could not install interface module",
+ "custom_external_modules_empty": "No custom modules installed yet.",
+ "custom_external_module_delete": "Delete",
+ "custom_external_module_deleted": "Interface module deleted",
+ "custom_external_module_delete_failed": "Could not delete interface module",
+ "custom_external_type_label": "Interface type name",
+ "custom_external_type_placeholder": "WeaveInterface",
+ "custom_external_json_label": "Additional options (JSON object)",
+ "custom_external_json_placeholder": "\"listen_ip\": \"0.0.0.0\", \"listen_port\": 4242",
+ "custom_external_docs_hint": "See the Interfaces manual for native types and external modules.",
+ "custom_external_docs_link": "Open bundled Reticulum manual",
+ "custom_external_type_required": "Enter the Reticulum interface type name.",
+ "custom_external_json_invalid": "Invalid JSON for extra options.",
+ "failed_save_discovery": "Failed to save discovery settings",
+ "no_interfaces_found_config": "No interfaces were found in the selected configuration file",
+ "failed_parse_config": "Failed to parse configuration file",
+ "select_config_file": "Please select a configuration file",
+ "select_at_least_one": "Please select at least one interface to import",
+ "import_success": "Interfaces imported successfully. MeshChat must be restarted for these changes to take effect.",
+ "failed_import_all": "Failed to import interfaces",
+ "add_interface_sidebar_a11y": "Shortcuts and community presets",
+ "community_quick_start": "Community Quick-Start",
+ "community_quick_start_hint": "Presets from directory.rns.recipes online listings.",
+ "community_quick_start_hide": "Hide suggested interfaces",
+ "community_use_preset": "Use This",
+ "community_presets_hidden_hint": "Community presets are hidden. Show them again here or in Settings.",
+ "community_presets_show_again": "Show community presets",
+ "community_presets_empty": "No presets loaded. Rebuild the bundle or add public/community_interfaces.json.",
+ "community_presets_refresh": "Refresh community presets from directory.rns.recipes",
+ "community_presets_refreshed": "Updated {count} community interface preset(s)",
+ "community_presets_refresh_failed": "Could not refresh community presets",
+ "i2p_requirements_title": "I2P interface rules",
+ "i2p_requirements_body": "Only one I2P interface is allowed. Enable Transport Mode in Settings first, then add I2P last. Do not import I2P from a config file or edit it in the raw config editor. Changes that leave I2P mid-list are repaired automatically on save and startup.",
+ "i2p_transport_required": "Enable Transport Mode in Settings before adding an I2P interface.",
+ "i2p_already_exists": "An I2P interface already exists. Remove it before adding another.",
+ "i2p_import_forbidden": "I2P interfaces cannot be imported from a file. Add I2P only through Add Interface.",
+ "find_more_nodes": "Find more nodes",
+ "quick_import": "Quick Import",
+ "quick_import_paste_hint": "Paste raw config",
+ "quick_import_placeholder": "[Interface Name]\ntype = TCPClientInterface\ntarget_host = 1.2.3.4",
+ "quick_import_apply": "Apply: {name}",
+ "discovered_network_name": "Network Name",
+ "discovered_passphrase": "Passphrase",
+ "discovered_copy_network_name": "Copy IFAC network name",
+ "discovered_copy_passphrase": "Copy IFAC passphrase",
+ "discovered_use_this": "Use this interface",
+ "discovered_copy_config": "Copy config block",
+ "discovered_config_block": "Config block",
+ "discovered_no_config": "This announce did not include a ready-to-paste config block.",
+ "discovered_prefill_applied": "Prefilled fields from discovered interface.",
+ "rnode_ble_toggle": "Bluetooth LE (RNode UART)",
+ "rnode_ble_hint": "Reticulum uses Bluetooth on this device. Pair the RNode in your OS settings first. This requires the Desktop or Android app. On Android, you must also allow the required permissions.",
+ "rnode_ble_peer_label": "BLE name or MAC",
+ "rnode_ble_peer_placeholder": "MyRNode or aa:bb:cc:dd:ee:ff",
+ "rnode_ble_peer_required": "Enter a BLE advertised name or MAC address for the RNode.",
+ "rnode_ip_host_label": "Host or IP address",
+ "rnode_tcp_port_fixed_hint": "Reticulum uses port 7633 on the RNode. Enter only the hostname or IP without the port.",
+ "rnode_tcp_host_required": "Enter the hostname or IP for RNode over TCP.",
+ "listen_ip_required": "Listen IP is required.",
+ "kernel_iface_picker_title": "Interfaces on this host",
+ "kernel_iface_loading": "Loading…",
+ "kernel_iface_picker_help": "Device is optional: one kernel interface name, or leave empty to bind using Listen IP only. Click a row to fill the field.",
+ "auto_iface_ifname_chips_hint": "This host's interfaces: click to add or remove names in the field above."
+ },
+ "map": {
+ "title": "Map",
+ "pop_out": "Pop out map",
+ "description": "Offline map tiles (MBTiles) and your own layers.",
+ "upload_mbtiles": "Upload MBTiles",
+ "select_file": "Select MBTiles file",
+ "offline_mode": "Offline Mode",
+ "online_mode": "Online Mode",
+ "attribution": "Attribution",
+ "bounds": "Bounds",
+ "center": "Center",
+ "zoom": "Zoom",
+ "uploading": "Uploading...",
+ "upload_success": "Map uploaded successfully",
+ "upload_failed": "Failed to upload map",
+ "invalid_file": "Invalid MBTiles file. Only raster tiles are supported.",
+ "default_view": "Default View",
+ "set_as_default": "Set as Default View",
+ "export_area": "Export Area",
+ "export_instructions": "Drag on the map to select an area.",
+ "min_zoom": "Min Zoom",
+ "max_zoom": "Max Zoom",
+ "tile_count": "Estimated Tiles",
+ "start_export": "Start Export",
+ "exporting": "Exporting Map...",
+ "download_ready": "Export Complete",
+ "download_now": "Download MBTiles",
+ "caching_enabled": "Tile Caching",
+ "clear_cache": "Clear Cache",
+ "cache_cleared": "Tile cache cleared",
+ "tile_server_url": "Tile Server URL",
+ "tile_server_url_placeholder": "https://tiles.openfreemap.org/styles/bright",
+ "tile_server_url_hint": "Use {z}, {x}, {y} for zoom, column, row",
+ "tile_server_saved": "Tile server URL saved",
+ "tile_server_openstreetmap": "OpenStreetMap",
+ "nominatim_api_url": "Nominatim API URL",
+ "nominatim_api_url_placeholder": "https://nominatim.openstreetmap.org",
+ "nominatim_api_url_hint": "Base URL for Nominatim geocoding service",
+ "nominatim_api_saved": "Nominatim API URL saved",
+ "search_placeholder": "Search for a location...",
+ "search_offline_error": "Search is only available in online mode",
+ "search_connection_error": "Failed to connect to search service. Please check your internet connection.",
+ "search_timeout_error": "The geocoder did not respond in time. It may be slow, overloaded, or unreachable. Try again.",
+ "search_error": "Search error",
+ "tile_connectivity_title": "Map tiles could not be loaded",
+ "tile_connectivity_body": "Check your network, try again, use a local tile server in settings, or switch to offline mode if you have MBTiles loaded.",
+ "tile_connectivity_retry": "Retry tiles",
+ "tile_connectivity_use_offline": "Use offline mode",
+ "tile_connectivity_dismiss": "Dismiss",
+ "tile_failover_trying": "Map tiles failed. Trying {provider}…",
+ "tile_failover_offline": "Online tiles unavailable. Switching to offline mode",
+ "search_no_results": "No results found",
+ "custom_tile_server_unavailable": "Custom tile server is not accessible in offline mode",
+ "custom_nominatim_unavailable": "Custom Nominatim server is not accessible in offline mode",
+ "onboarding_title": "Export to MBTiles!",
+ "onboarding_text": "Use the export tool to download map areas as MBTiles files for offline use.",
+ "onboarding_got_it": "Got it",
+ "tool_point": "Draw Point",
+ "tool_linestring": "Draw Line",
+ "tool_polygon": "Draw Polygon",
+ "tool_circle": "Draw Circle",
+ "tool_measure": "Measure Distance/Area",
+ "tool_bearing": "Bearing and distance (two taps, offline)",
+ "tool_bearing_from_here": "Bearing from my location to one tap",
+ "bearing_geodesic": "Geodesic (great circle)",
+ "bearing_rhumb": "Rhumb line (constant bearing)",
+ "bearing_forward": "Forward azimuth",
+ "bearing_back": "Back azimuth",
+ "bearing_rhumb_line": "Rhumb bearing / back",
+ "bearing_hint_first": "Tap the start point, then the end point.",
+ "bearing_hint_second": "Tap the end point.",
+ "bearing_hint_destination": "Tap your destination on the map.",
+ "bearing_from_here": "Use my location as start",
+ "bearing_help_first": "Tap start, then end",
+ "bearing_help_drag": "Move the pointer for a live readout",
+ "tool_clear": "Clear Drawings",
+ "save_drawing": "Save Drawing Layer",
+ "load_drawing": "Load Drawing Layer",
+ "save_drawing_title": "Save Drawing Layer",
+ "save_drawing_desc": "Save your current map annotations to the database.",
+ "drawing_name": "Layer Name",
+ "drawing_name_placeholder": "e.g., Target Area Alpha",
+ "load_drawing_title": "Load Drawing Layer",
+ "loading_drawings": "Loading saved layers...",
+ "no_drawings": "No Saved Layers",
+ "no_drawings_desc": "You haven't saved any map annotations yet.",
+ "saved_on": "Saved on",
+ "settings": "Map Settings",
+ "go_to_my_location": "Go to My Location",
+ "source_updated": "Map source updated",
+ "failed_set_active": "Failed to set active map",
+ "file_deleted": "File deleted",
+ "failed_delete_file": "Failed to delete file",
+ "storage_saved": "Storage directory saved",
+ "failed_save_storage": "Failed to save directory",
+ "export_cancelled": "Export cancelled",
+ "failed_cancel_export": "Failed to cancel export",
+ "failed_start_export": "Failed to start export",
+ "select_mbtiles_error": "Please select an .mbtiles file",
+ "view_saved": "Default view saved",
+ "failed_save_view": "Failed to save default view",
+ "failed_clear_cache": "Failed to clear cache",
+ "failed_save_tile_server": "Failed to save tile server URL",
+ "failed_save_nominatim": "Failed to save Nominatim API URL",
+ "copied_coordinates": "Copied coordinates",
+ "failed_load_drawings": "Failed to load drawings",
+ "not_initialized": "Map not initialized",
+ "drawing_saved": "Drawing saved",
+ "failed_save_drawing": "Failed to save drawing",
+ "deleted": "Deleted",
+ "failed_delete": "Failed to delete",
+ "location_not_determined": "Could not determine your location",
+ "geolocation_not_supported": "Geolocation is not supported by your browser",
+ "no_nodes_location": "No discovered nodes with location found",
+ "failed_fetch_nodes": "Failed to fetch discovered nodes for mapping",
+ "share_view": "Share map view (copy links)",
+ "share_message_prefix": "MeshChatX map:",
+ "share_copied": "Map links copied to clipboard",
+ "ping_here": "Ping here (send in chat)",
+ "ping_here_toolbar": "Ping map center in chat",
+ "ping_modal_title": "Send map ping",
+ "ping_destination": "Destination",
+ "ping_pick_conversation": "Choose a conversation",
+ "ping_send": "Send ping",
+ "ping_sent": "Map ping sent",
+ "ping_failed": "Could not send map ping",
+ "ping_invalid_destination": "Pick a valid 32-character destination hash",
+ "ping_message_prefix": "MeshChatX map ping:",
+ "north_up": "North is up (map not rotated)",
+ "export_tile_limit_exceeded": "Too many tiles for one export. Lower max zoom or shrink the area.",
+ "export_region_world": "World (low zoom)",
+ "export_region_europe": "Europe",
+ "export_region_north_america": "North America",
+ "export_region_south_america": "South America",
+ "export_region_africa": "Africa",
+ "export_region_asia": "Asia",
+ "export_region_oceania": "Oceania",
+ "vector_exchange_title": "GeoJSON / KML / KMZ",
+ "vector_exchange_merge": "Merge with existing drawings (uncheck to replace)",
+ "vector_exchange_hint": "Imports support icons from various formats including GeoJSON and KML. Select a drawing to see its metadata.",
+ "vector_import_geojson": "Import GeoJSON",
+ "vector_import_kml": "Import KML",
+ "vector_import_kmz": "Import KMZ",
+ "vector_export_geojson": "Export GeoJSON",
+ "vector_export_kml": "Export KML",
+ "vector_export_kmz": "Export KMZ",
+ "vector_import_ok": "Imported {count} feature(s).",
+ "vector_import_empty": "No features found in file.",
+ "vector_import_failed": "Could not read vector file.",
+ "vector_export_ok": "Export started.",
+ "remote_overlays_title": "Remote overlays",
+ "remote_overlays_reload": "Reload",
+ "remote_overlays_kind": "Source type",
+ "remote_overlays_url": "Source URL",
+ "remote_overlays_paths": "Repo file paths (one per line)",
+ "remote_overlays_ref": "Git ref (branch, tag, or commit)",
+ "remote_overlays_refresh_interval": "Autorefresh interval (seconds, 0 = off)",
+ "remote_overlays_import": "Import / fetch",
+ "remote_overlays_importing": "Fetching…",
+ "remote_overlays_empty": "No remote overlays yet.",
+ "remote_overlays_visible": "Show",
+ "remote_overlays_refresh": "Refresh",
+ "remote_overlays_copy_drawings": "Copy to drawings",
+ "remote_overlays_delete": "Delete",
+ "remote_overlays_error": "Remote overlay error",
+ "remote_overlays_export_ok": "Overlay export started.",
+ "remote_overlays_export_failed": "Overlay export failed.",
+ "remote_overlays_copied": "Copied overlay into drawings.",
+ "drop_geo_files": "Drop map file here",
+ "drop_map_files_hint": "GeoJSON, KML, KMZ, or MBTiles",
+ "drop_no_geo_files": "No GeoJSON, KML, or KMZ files detected.",
+ "drop_no_supported_files": "No GeoJSON, KML, KMZ, or MBTiles files detected.",
+ "new_tab": "New map",
+ "new_tab_shortcut": "New map (Ctrl+T)",
+ "tab_default_name": "Map {number}",
+ "tab_rename_hint": "Double-click or double-tap to rename"
+ },
+ "interface": {
+ "disable": "Disable",
+ "enable": "Enable",
+ "edit_interface": "Edit Interface",
+ "export_interface": "Export Interface",
+ "delete_interface": "Delete Interface",
+ "listen": "Listen",
+ "forward": "Forward",
+ "port": "Port",
+ "frequency": "Frequency",
+ "bandwidth": "Bandwidth",
+ "txpower": "TX Power",
+ "spreading_factor": "SF",
+ "coding_rate": "Coding Rate",
+ "bitrate": "Bitrate",
+ "tx": "TX",
+ "rx": "RX",
+ "noise": "Noise",
+ "clients": "Clients",
+ "link_up": "Link up",
+ "link_down": "Link down",
+ "link_unknown": "Link status unknown"
+ },
+ "messages": {
+ "title": "Messages",
+ "conversations": "Conversations",
+ "announces": "Announces",
+ "search_placeholder": "Search {count} conversations...",
+ "unread": "Unread",
+ "failed": "Failed",
+ "attachments": "Attachments",
+ "no_messages_yet": "No messages yet",
+ "loading_conversations": "Loading conversations...",
+ "no_conversations": "No Conversations",
+ "discover_peers": "Discover peers on the Announces tab",
+ "no_search_results": "No Results Found",
+ "try_another_search": "Try a different search term",
+ "no_search_results_conversations": "Your search didn't match any conversations.",
+ "search_placeholder_announces": "Search {count} recent announces...",
+ "no_peers_discovered": "No Peers Discovered",
+ "waiting_for_announce": "Waiting for someone to announce!",
+ "no_search_results_peers": "Your search didn't match any Peers!",
+ "searching_announces": "Searching announces...",
+ "direct": "Direct",
+ "downloading": "Downloading",
+ "hops": "{count} hops",
+ "hops_away": "{count} hops away",
+ "snr": "SNR {snr}",
+ "stamp_cost": "Stamp Cost {cost}",
+ "stamp_ticket_valid": "Outbound stamp ticket active (instant send). Expires {expires}.",
+ "stamp_ticket_expired": "Stamp ticket expired. The next message may require proof-of-work.",
+ "pop_out_chat": "Pop out chat",
+ "open_in_split": "Open in split view",
+ "select_conversation_for_pane": "Select a conversation to open it here",
+ "more_actions": "More actions",
+ "retry_failed": "Retry failed messages",
+ "telemetry_history": "Telemetry history",
+ "telemetry_history_modal_title": "Telemetry History",
+ "telemetry_history_empty": "No telemetry history found for this peer.",
+ "telemetry_history_done": "Done",
+ "telemetry_show_in_chat": "Show telemetry in main chat",
+ "telemetry_battery_trend": "Battery trend",
+ "telemetry_battery_range": "{from}% → {to}%",
+ "telemetry_label_sent": "Sent",
+ "telemetry_label_received": "Received",
+ "telemetry_battery_level": "Battery: {percent}%",
+ "telemetry_snr_db": "SNR: {snr} dB",
+ "telemetry_location_request": "Location Request",
+ "start_call": "Call",
+ "share_contact": "Share contact",
+ "share_contact_modal_title": "Share contact",
+ "share_contact_search_placeholder": "Search contacts…",
+ "share_apk": "Share app (APK)",
+ "share_apk_failed": "Could not open the share sheet for the APK.",
+ "custom_display_name": "Custom Display Name",
+ "stranger_banner_text": "This peer is not in your contacts. Attachments from strangers are blocked.",
+ "add_to_contacts": "Add to Contacts",
+ "dismiss": "Dismiss",
+ "attachments_stripped_notice": "Attachments were blocked from this unknown sender.",
+ "stranger_link_open_confirm": "This link was sent by an unknown contact:\n{url}\n\nOpen it anyway?",
+ "send_placeholder": "Type a message...",
+ "compose_hint_automatic": "Message for automatic delivery",
+ "compose_hint_direct": "Message for direct delivery",
+ "compose_hint_opportunistic": "Message for opportunistic delivery",
+ "compose_hint_propagated": "Message for propagated delivery",
+ "send_menu_more_label": "Other Sending Options",
+ "send_menu_telemetry_request": "Send as Command or Request",
+ "send_menu_paper_compose": "Paper message from composition (LXM)",
+ "sending_ellipsis": "Sending…",
+ "outbound_pending_propagation": "Sending to propagation node…",
+ "outbound_pending_propagation_with_progress": "Sending to propagation node… {progress}%",
+ "outbound_solving_stamps": "Solving stamps…",
+ "outbound_solving_stamps_short": "Solving stamps",
+ "outbound_pathfinding_tooltip": "Finding path: Reticulum is resolving a route to this peer. Your message sends when the route is ready.",
+ "outbound_pathfinding_short": "Finding path",
+ "path_no_route": "No path",
+ "path_no_route_hint": "No route to this peer yet. Use the path finder or send a message to discover one.",
+ "path_loading": "Checking path…",
+ "path_stale_label": "stale",
+ "path_unresponsive_label": "unresponsive",
+ "path_stale_hint": "This path entry is expired and may be rediscovered before the next send.",
+ "path_unresponsive_hint": "Reticulum marked this path as unresponsive.",
+ "path_info_title": "Path information",
+ "path_already_available": "Path is already available",
+ "outbound_preparing_message": "Preparing message…",
+ "outbound_preparing_message_short": "Preparing message",
+ "outbound_sending_with_progress": "Sending… {progress}%",
+ "transfer_progress_hop_one": "1 hop",
+ "transfer_progress_hops": "{count} hops",
+ "outbound_sending_short": "Sending",
+ "outbound_pending": "Pending",
+ "outbound_outbound_short": "Outbound",
+ "send_pathfinding_tooltip": "Finding path to peer (Reticulum is resolving a route). Your message sends as soon as the route is ready. Hover status icons in the bubble for details.",
+ "outbound_on_propagation_node": "On propagation node",
+ "outbound_sent_network": "Sent",
+ "outbound_delivered": "Delivered",
+ "outbound_delivered_propagated": "Delivered via propagation",
+ "no_messages_in_conversation": "No messages in this conversation yet.",
+ "say_hello": "Say hello!",
+ "no_active_chat": "No Active Chat",
+ "mobile_compose_title": "New message",
+ "mobile_compose_destination_placeholder": "32-character destination hash…",
+ "select_peer_or_enter_address": "Select a peer from the sidebar or enter an address below",
+ "add_files": "Add Files",
+ "paste_from_clipboard": "Paste from Clipboard",
+ "location": "Location",
+ "share_location": "Share Location",
+ "request_location": "Request Location",
+ "view_on_map": "View on Map",
+ "request": "Request",
+ "generate_paper_message": "Generate Paper Message (LXM)",
+ "ingest_paper_message": "Ingest Paper Message",
+ "scan_qr": "Scan QR Code",
+ "scanner_hint": "Point your camera at an lxmf://, lxma://, or lxm:// QR code.",
+ "camera_not_supported": "Camera QR scanning is not supported in this browser.",
+ "camera_permission_denied": "Camera permission denied. Please allow camera access.",
+ "camera_not_found": "No camera device found.",
+ "camera_failed": "Failed to start camera scanner.",
+ "invalid_qr_uri": "Invalid QR content. Expected an lxmf://, lxma://, or lxm:// URI.",
+ "recording": "Recording: {duration}",
+ "nomad_network_node": "Nomad Network Node",
+ "toggle_source": "Toggle Source Code",
+ "folder_created": "Folder created",
+ "failed_create_folder": "Failed to create folder",
+ "folder_renamed": "Folder renamed",
+ "failed_rename_folder": "Failed to rename folder",
+ "folder_deleted": "Folder deleted",
+ "failed_delete_folder": "Failed to delete folder",
+ "moved_to_folder": "Moved to folder",
+ "failed_move_folder": "Failed to move to folder",
+ "mark_as_read": "Mark as Read",
+ "mark_all_as_read": "Mark all as read",
+ "marked_read": "Marked as read",
+ "marked_all_read": "All conversations marked as read",
+ "failed_mark_read": "Failed to mark as read",
+ "conversations_deleted": "Conversations deleted",
+ "failed_delete_conversations": "Failed to delete conversations",
+ "failed_export_folders": "Failed to export folders",
+ "folders_imported": "Folders imported",
+ "failed_import_folders": "Failed to import folders",
+ "failed_read_clipboard": "Failed to read from clipboard",
+ "clipboard_read_requires_secure_context": "Clipboard read needs a secure page (https or http://127.0.0.1). Using http://0.0.0.0 or another non-local HTTP origin blocks the Clipboard API. Open the app on 127.0.0.1 or https, or paste with Ctrl+V.",
+ "clipboard_write_unavailable": "Could not copy automatically. Open on https or http://127.0.0.1, or select the text and copy manually.",
+ "oversized_body_notice": "This message is too large to display here ({count} characters). Copy the full text to read or edit it elsewhere.",
+ "oversized_body_copy": "Copy full text",
+ "oversized_body_copied": "Full message text copied",
+ "date_divider_today": "Today",
+ "date_divider_yesterday": "Yesterday",
+ "raw_path_interface_at_send": "Path interface (when recorded)",
+ "raw_path_hops_at_send": "Path hops (when recorded)",
+ "raw_path_value_unknown": "Unknown",
+ "raw_path_finding_measure": "Pathfinding at send (outbound)",
+ "raw_path_row_hash_rnpath": "Path table hash (rnpath)",
+ "raw_path_row_hash_rnpath_hint": "The destination hash used by Reticulum for this route. You can find it in the path table.",
+ "copy_message": "Copy message",
+ "message_copied": "Message copied",
+ "failed_copy_message": "Could not copy message",
+ "failed_send_ingest": "Failed to send ingest request",
+ "address_copied": "Your LXMF address copied to clipboard",
+ "map_link_share_title": "Shared map view",
+ "relay_link_hub_title": "Shared Relay Chat hub",
+ "relay_link_room_title": "Shared Relay Chat room",
+ "relay_link_join": "Join in Relay Chat",
+ "relay_link_copy_uri": "Copy relay link",
+ "relay_link_copied": "Relay link copied",
+ "relay_link_opened": "Opened in Relay Chat",
+ "relay_link_invalid": "Invalid relay link",
+ "relay_link_failed": "Could not open relay link",
+ "relay_link_disabled": "Relay Chat is disabled in settings",
+ "unknown_meshchatx_link": "Unknown meshchatx link ({host})",
+ "unknown_meshchatx_link_generic": "Unknown or invalid meshchatx link",
+ "map_link_ping_title": "Map ping",
+ "map_link_open": "Open on map",
+ "map_link_copy_uri": "Copy meshchatx link",
+ "map_link_copied": "Map link copied",
+ "hash_copied": "Identity hash copied to clipboard",
+ "failed_to_copy_hash": "Failed to copy identity hash",
+ "copy_hash": "Copy Hash",
+ "failed_copy_address": "Failed to copy address",
+ "translation_failed": "Translation failed",
+ "translate_select_target": "Translate to",
+ "translating_message": "Translating...",
+ "translated_from_to": "Translated from {source} to {target}",
+ "show_original": "Show original",
+ "show_translation": "Show translation",
+ "translate_message": "Translate message",
+ "translate_target_invalid": "Choose a different target language than the source",
+ "failed_add_contact": "Failed to add contact",
+ "ingesting_paper_message": "Ingesting paper message...",
+ "failed_ingest_paper": "Failed to ingest paper message",
+ "failed_to_send": "Failed to send message",
+ "failed_to_send_image": "Failed to send image {index}: {detail}",
+ "enter_display_name": "Enter a custom display name",
+ "failed_update_display_name": "Failed to update display name",
+ "failed_load_audio": "Failed to load audio attachment.",
+ "no_contacts_telephone": "No contacts found in telephone",
+ "failed_load_contacts": "Failed to load contacts",
+ "location_request_sent": "Location request sent",
+ "failed_send_location_request": "Failed to send location request",
+ "fetching_location": "Fetching location...",
+ "location_disabled": "Location sharing is disabled. Enable it in Settings > Location.",
+ "location_sent": "Location shared successfully",
+ "location_requested": "Location requested",
+ "remove_image_confirm": "Are you sure you want to remove this image attachment?",
+ "failed_start_recording": "failed to start recording",
+ "failed_start_recording_help_mediadevices": "This web view does not expose a microphone API.",
+ "failed_start_recording_help_web_audio": "Web Audio is not available in this browser.",
+ "failed_start_recording_help_audio_worklet": "AudioWorklet is not supported. OPUS voice notes require it.",
+ "failed_start_recording_help_permission": "Allow microphone access, or try again after another app releases the microphone.",
+ "remove_audio_confirm": "Are you sure you want to remove this audio attachment?",
+ "failed_generate_qr": "Failed to generate QR",
+ "enter_folder_name": "Enter folder name",
+ "new_folder": "New Folder",
+ "rename_folder": "Rename folder",
+ "failed_render_qr": "Failed to render QR code",
+ "uri_copied": "URI copied to clipboard",
+ "failed_copy_uri": "Failed to copy URI",
+ "paper_message_sent": "Paper message sent successfully",
+ "failed_send_paper": "Failed to send paper message",
+ "failed_load_config": "Failed to load configuration",
+ "profile_icon_saved": "Profile icon saved successfully",
+ "failed_save_profile_icon": "Failed to save profile icon",
+ "select_colors_warning": "Please select both background and icon colors",
+ "select_icon_warning": "Please select an icon",
+ "changes_reset": "Changes reset to saved values",
+ "profile_icon_removed": "Profile icon removed successfully",
+ "user_banished": "User banished successfully",
+ "failed_banish_user": "Failed to banish user",
+ "banish_user": "Banish user",
+ "banish_confirm": "Are you sure you want to banish this user? They will not be able to send you messages or establish links.",
+ "delete_conversation_confirm": "Are you sure you want to delete this conversation?",
+ "failed_delete_conversation": "failed to delete conversation",
+ "delete_history_confirm": "Are you sure you want to delete all messages in this conversation? This can not be undone!",
+ "failed_delete_history": "failed to delete history",
+ "pin_conversation": "Pin conversation",
+ "unpin_conversation": "Unpin conversation",
+ "failed_toggle_pin": "Could not update pin",
+ "opportunistic_deferred_label": "Waiting",
+ "opportunistic_deferred_tooltip": "Message will send as soon as the user comes online or announces.",
+ "failed_waiting_announce": "Failed, waiting for announce",
+ "failed_waiting_announce_tooltip": "Delivery failed. Will resend automatically when the contact is reachable.",
+ "invalid_destination_hash": "Invalid destination hash",
+ "invalid_destination_hash_format": "Invalid destination hash format",
+ "ping_failed": "Ping failed. Try again later",
+ "ping_in_progress": "Pinging destination",
+ "ping_reply_from": "Valid reply from {hash}",
+ "duration": "Duration: {duration}",
+ "hops_there": "Hops There: {count}",
+ "hops_back": "Hops Back: {count}",
+ "signal_quality": "Signal Quality: {quality}%",
+ "rssi_val": "RSSI: {rssi}dBm",
+ "snr_val": "SNR: {snr}dB",
+ "reply": "Reply",
+ "replying_to": "Replying to",
+ "forward_banner_label": "Forwarded message",
+ "forward_you_shared": "You're forwarding this quote",
+ "forward_pick_recipient_title": "Forward to contact",
+ "forward_message": "Forward",
+ "forward_sent": "Message forwarded",
+ "forward_failed": "Could not forward message",
+ "forward_no_lxmf_destination": "This contact has no usable LXMF delivery address.",
+ "forward_verify_show": "Source and verification",
+ "forward_verify_hide": "Hide verification",
+ "forward_verify_explanation": "Paper LXMF links only work if the full message is available and fits Paper limits. Quoted snippets can be faked.",
+ "forward_verify_loading": "Looking up verification URI…",
+ "forward_verify_open_uri": "Open verification URI",
+ "forward_verify_unavailable": "Original message bytes are not cached here, so no verification URI could be built.",
+ "forward_verify_not_cached": "Original message bytes are not cached here.",
+ "forward_verify_failed": "Verification lookup failed.",
+ "forward_content_stub": "_Forwarded via MeshChatX_\nAttachments and quoted text belong to the original sender.",
+ "forward_open_party_is_self": "That is your own address.",
+ "message_actions": "Message actions",
+ "save_image_to_device": "Save image to device",
+ "cancel_send": "Cancel send",
+ "react": "React",
+ "reaction_you": "You",
+ "reaction_send_failed": "Could not send reaction",
+ "conversation_reaction_preview": "{name} reacted {emoji}",
+ "conversation_location_share_you": "You shared your location",
+ "conversation_location_share_other": "{name} shared their location",
+ "conversation_telemetry_preview": "{name} sent telemetry",
+ "conversation_telemetry_stream_preview": "{name} sent a telemetry stream",
+ "conversation_location_request_in_preview": "{name} requested your location",
+ "conversation_location_request_out_preview": "{name} sent a location request",
+ "conversation_image_you": "You sent an image",
+ "conversation_image_other": "{name} sent an image",
+ "conversation_voice_you": "You sent a voice note",
+ "conversation_voice_other": "{name} sent a voice note",
+ "conversation_file_you": "You sent a file",
+ "conversation_file_other": "{name} sent a file",
+ "conversation_files_you": "You sent {count} files",
+ "conversation_files_other": "{name} sent {count} files",
+ "message_not_found_in_cache": "Message not found in cache"
+ },
+ "settings": {
+ "tabs": {
+ "general": "General",
+ "general_desc": "Language, appearance, battery saver, platform, maps, and shortcuts",
+ "messages": "Messages",
+ "messages_desc": "LXMF, stickers, and safety",
+ "network": "Network",
+ "network_desc": "Transport, interfaces, discovery, calls, and security",
+ "nomad": "NomadNet",
+ "nomad_desc": "Archiver and page rendering",
+ "privacy": "Privacy",
+ "privacy_desc": "Data, access, security, and banished users",
+ "maintenance": "Maintenance",
+ "maintenance_desc": "Cleanup, export, import, and sources",
+ "plugins": "Plugins",
+ "plugins_desc": "Install and manage MeshChatX plugins"
+ },
+ "shortcut_saved": "Shortcut saved",
+ "shortcut_deleted": "Shortcut deleted",
+ "keyboard_shortcuts_title": "Keyboard Shortcuts",
+ "keyboard_shortcuts_description": "Customize quick keyboard actions. Collapsed by default on phones.",
+ "archived_pages_flushed": "Archived pages flushed.",
+ "failed_enable_transport": "Failed to enable transport mode!",
+ "failed_disable_transport": "Failed to disable transport mode!",
+ "failed_update_reticulum_instance": "Failed to update Reticulum instance settings!",
+ "failed_reload_reticulum": "Failed to reload Reticulum!",
+ "folders_exported": "Folders exported",
+ "failed_export_folders": "Failed to export folders",
+ "folders_imported": "Folders and mappings imported successfully",
+ "failed_import_folders": "Failed to import folders",
+ "share_apk_heading": "Share this install",
+ "share_apk_desc": "Send the installed MeshChatX package over Bluetooth, Nearby Share, or another app. Only available in the Android app.",
+ "share_apk_short_hint": "Opens the system share sheet for this APK.",
+ "share_apk": "Share app (APK)",
+ "share_apk_failed": "Could not open the share sheet for the APK.",
+ "android_privacy_heading": "Device privacy",
+ "android_privacy_desc": "Optional Android shell protections. All options are off by default so normal screenshots and clipboard use keep working.",
+ "android_block_screenshots": "Block screenshots and recent-app previews",
+ "android_block_screenshots_desc": "Uses the Android secure-window flag so screenshots and the app switcher preview cannot capture MeshChatX content.",
+ "android_block_screenshots_on": "Screenshot blocking enabled",
+ "android_block_screenshots_off": "Screenshot blocking disabled",
+ "android_clear_clipboard_on_background": "Clear clipboard when leaving the app",
+ "android_clear_clipboard_on_background_desc": "Clears the system clipboard when MeshChatX goes to the background so copied message text is harder to leak to other apps.",
+ "android_clear_clipboard_on_background_on": "Clipboard will clear when the app backgrounds",
+ "android_clear_clipboard_on_background_off": "Clipboard will no longer clear on background",
+ "android_privacy_save_failed": "Could not update Android privacy setting.",
+ "battery": {
+ "eyebrow": "Power",
+ "title": "Battery saver",
+ "description": "Reduce UI polling and visualiser work on battery. Messaging and mesh delivery stay active. Each option below applies only while battery saver is on.",
+ "enabled": "Enable battery saver",
+ "enabled_desc": "Master switch for the power-saving options below.",
+ "options_heading": "When battery saver is on",
+ "disable_visualiser_discovery": "Hide discovered interfaces in visualiser",
+ "disable_visualiser_discovery_desc": "Stops drawing discovered-interface nodes on the network graph.",
+ "hide_offline_interfaces": "Hide offline interfaces in visualiser",
+ "hide_offline_interfaces_desc": "Only show online (and path-table) interfaces on the graph.",
+ "max_visualiser_interfaces": "Max interfaces on visualiser",
+ "max_visualiser_interfaces_desc": "0 means unlimited. Prefer online interfaces when truncating.",
+ "visualiser_reload_seconds": "Visualiser auto-reload interval (seconds)",
+ "visualiser_reload_seconds_desc": "0 disables auto-reload while saver is on. Default without saver is 15s.",
+ "disable_visualiser_live_layout": "Disable visualiser live layout",
+ "disable_visualiser_live_layout_desc": "Turns off continuous layout/physics work on the graph.",
+ "reduce_background_polling": "Slow background status polling",
+ "reduce_background_polling_desc": "Multiplies App shell poll intervals (calls, unread, app info).",
+ "background_poll_multiplier": "Background poll multiplier",
+ "background_poll_multiplier_desc": "2-10. Applied to App shell intervals when the option above is on.",
+ "reduce_interfaces_discovery": "Slow Interfaces page polls",
+ "reduce_interfaces_discovery_desc": "Uses the intervals below for stats and discovered-interface refresh.",
+ "interfaces_stats_poll_seconds": "Interfaces stats poll (seconds)",
+ "interfaces_discovery_poll_seconds": "Discovered interfaces poll (seconds)",
+ "apply_interface_bitrate_limits": "Force interface bitrates",
+ "apply_interface_bitrate_limits_desc": "Write forced bitrate (bps) into selected Reticulum interfaces and reload RNS when applied.",
+ "interface_bitrate_limits_help": "Enter a bitrate in bits per second for each interface you want to cap. Leave blank to skip. Apply writes the config and reloads Reticulum.",
+ "interface_bitrate_limits_empty": "No interfaces found yet.",
+ "interface_bitrate_placeholder": "bps",
+ "apply_bitrates_reload": "Apply bitrates and reload RNS",
+ "restore_bitrates_reload": "Restore previous bitrates and reload",
+ "bitrates_applied": "Updated {count} interface bitrate(s) and reloaded RNS",
+ "bitrates_restored": "Restored {count} interface bitrate(s) and reloaded RNS",
+ "bitrates_none_applied": "No bitrate limits matched current interfaces",
+ "bitrates_none_restored": "No previous bitrates to restore",
+ "bitrates_apply_failed": "Failed to apply interface bitrates",
+ "bitrates_restore_failed": "Failed to restore interface bitrates"
+ },
+ "nomad_micron_wasm_title": "Micron (WASM)",
+ "nomad_micron_wasm_desc_before_link": "Use a faster engine (",
+ "nomad_micron_wasm_link_label": "micron-parser-go",
+ "nomad_micron_wasm_desc_after_link": "). When on, the WebAssembly build can load and be updated in settings. When off, Micron uses JavaScript only. Pick the default renderer below. You can still switch from the Nomad browser toolbar.",
+ "nomad_micron_default_engine_title": "Default Micron renderer (.mu)",
+ "nomad_micron_default_engine_desc": "JavaScript is the default. WebAssembly uses micron-parser-go when the engine above is enabled and the module has loaded.",
+ "nomad_micron_default_engine_option_js": "JavaScript (micron-parser)",
+ "nomad_micron_default_engine_option_wasm": "WebAssembly (micron-parser-go)",
+ "micron_wasm_update_open_btn": "Update WASM engine…",
+ "micron_wasm_update_modal_title": "Micron WASM engine",
+ "micron_wasm_update_active_label": "Active override",
+ "micron_wasm_update_active_source": "Source: {source}",
+ "micron_wasm_update_source_github": "GitHub release (SHA-256 verified)",
+ "micron_wasm_update_source_upload": "Local file (not verified)",
+ "micron_wasm_update_active_size": "Size: {bytes} bytes",
+ "micron_wasm_update_bundled_only": "Using the WASM bundled with this app. Nothing is fetched from the network until you use the actions below.",
+ "micron_wasm_update_isolation_note": "The browser cannot fully isolate WebAssembly from JavaScript. This app only exposes Micron conversion through a narrow API. Rendered HTML is still sanitized. wasm_exec.js always comes from the app bundle to match the Go runtime.",
+ "micron_wasm_update_github_heading": "Install from GitHub release",
+ "micron_wasm_update_github_url_hint": "Press Fetch so the MeshChat server downloads from GitHub (browser stays same-origin for CSP). Source path:",
+ "micron_wasm_update_fetch_github": "Fetch and install",
+ "micron_wasm_update_upload_heading": "Install from file",
+ "micron_wasm_update_upload_warning": "Uploaded WASM is not authenticity-checked. Only use files you trust.",
+ "micron_wasm_update_revert_bundled": "Remove override (use bundled WASM)",
+ "micron_wasm_update_err_empty_tag": "Enter a release tag (for example v1.0.6).",
+ "micron_wasm_update_err_not_wasm": "Choose a file whose name ends with .wasm.",
+ "micron_wasm_update_err_too_large": "That file is too large to store as a runtime override.",
+ "micron_wasm_update_err_too_small": "That file is too small to be a valid WASM module.",
+ "micron_wasm_update_err_activate_failed": "Installed file could not be loaded (check wasm_exec compatibility or try another build). Override was removed.",
+ "micron_wasm_update_toast_installed": "Installed Micron WASM {tag}.",
+ "micron_wasm_update_toast_uploaded": "Installed WASM from your file.",
+ "micron_wasm_update_toast_reverted": "Restored bundled Micron WASM."
+ },
+ "debug": {
+ "title": "Debug Logs",
+ "description": "Read application and access logs, filter by severity, and search when troubleshooting.",
+ "tab_logs": "Logs",
+ "tab_access_attempts": "Access attempts",
+ "search_logs_placeholder": "Search logs...",
+ "search_access_placeholder": "Search IP, path, outcome...",
+ "level_all": "All levels",
+ "anomalies_only": "Anomalies only",
+ "outcome_all": "All outcomes",
+ "loading_logs": "Loading logs...",
+ "no_logs": "No logs found.",
+ "loading_access": "Loading access attempts...",
+ "no_access": "No access attempts recorded.",
+ "failed_fetch_logs": "Failed to fetch logs",
+ "failed_fetch_access": "Failed to fetch access attempts",
+ "logs_copied": "Logs on this page copied to clipboard",
+ "access_copied": "Access attempts copied to clipboard",
+ "failed_copy_logs": "Failed to copy logs",
+ "failed_copy_access": "Failed to copy access attempts"
+ },
+ "visualiser": {
+ "title": "Network Visualiser",
+ "description": "Graph of discovered nodes and links.",
+ "renderer_title": "Renderer",
+ "renderer_desc": "Auto prefers WebGL with WASM when available, then falls back to vis-network. Force a path for debugging or older GPUs.",
+ "renderer_option_auto_short": "Auto",
+ "renderer_option_webgl_short": "WebGL",
+ "renderer_option_vis_short": "Canvas",
+ "renderer_option_auto": "Auto (WebGL when available)",
+ "renderer_option_webgl": "WebGL + WASM",
+ "renderer_option_vis": "vis-network (canvas)",
+ "renderer_webgl_unavailable": "WebGL renderer unavailable. Using vis-network fallback.",
+ "reticulum_mesh": "Reticulum Mesh",
+ "network_visualizer": "Network Visualizer",
+ "node_info": "Node Info",
+ "hash": "Hash",
+ "hops": "Hops",
+ "interface": "Interface",
+ "rssi": "RSSI",
+ "snr": "SNR",
+ "first_seen": "First seen",
+ "last_seen": "Last seen",
+ "center_node": "Center on node",
+ "copy_hash": "Copy Hash",
+ "banish_identity": "Banish identity",
+ "banished_identities": "Banished identities",
+ "filter_nodes": "Filter nodes...",
+ "all": "All",
+ "direct": "Direct",
+ "one_hop": "1 Hop",
+ "multi_hops": "2+ Hops",
+ "clear": "Clear",
+ "show_labels": "Show all labels",
+ "show_edges": "Show edges",
+ "show_tooltips": "Show tooltips",
+ "show_rssi": "Show RSSI",
+ "show_snr": "Show SNR",
+ "show_hops": "Show Hops",
+ "physics": "Physics",
+ "gravity": "Gravity",
+ "spring_length": "Spring length",
+ "spring_strength": "Spring strength",
+ "damping": "Damping",
+ "central_gravity": "Central gravity",
+ "edge_strength": "Edge strength",
+ "force_atlas_2": "Force Atlas 2",
+ "barns_hut": "Barns Hut",
+ "repulsion": "Repulsion",
+ "reset_physics": "Reset physics",
+ "reset_camera": "Reset Camera",
+ "clear_graph": "Clear graph",
+ "total_nodes": "Total nodes",
+ "total_edges": "Total edges",
+ "discovered": "Discovered",
+ "wait_between_batches": "Wait between batches",
+ "batch": "Batch",
+ "loading_status": "Loading status",
+ "failed_load": "Failed to load network data",
+ "max_hops_filter": "Max hops",
+ "show_disabled_interfaces": "Show offline interfaces",
+ "show_discovered_interfaces": "Show discovered interfaces",
+ "refresh": "Refresh",
+ "engine": "Engine",
+ "engine_webgl": "WebGL",
+ "engine_wasm": "WASM",
+ "engine_fallback": "JS fallback",
+ "engine_checking": "Checking",
+ "engine_webgl_hint": "Graph, layout, and draw buffers run in Go WebAssembly. Pixels are drawn with WebGL2",
+ "engine_wasm_hint": "Graph build and force layout run in Go WebAssembly. Canvas draw stays in the browser",
+ "engine_fallback_hint": "WASM unavailable. Graph build uses JavaScript. Live layout uses vis-network physics",
+ "engine_checking_hint": "Detecting WebAssembly support",
+ "fps": "FPS",
+ "auto_update": "Auto Update",
+ "live_layout": "Live Layout",
+ "nodes": "Nodes",
+ "links": "Links",
+ "interfaces": "Interfaces",
+ "online": "Online",
+ "offline": "Offline",
+ "search_nodes_placeholder": "Search nodes ({count})...",
+ "clear_search": "Clear search"
+ },
+ "banishment": {
+ "title": "Banished",
+ "description": "Review and lift blocks on identities and nodes.",
+ "failed_load_banished": "Failed to load banished destinations",
+ "search_placeholder": "Search by hash or display name...",
+ "loading_items": "Loading banished items...",
+ "no_items": "No banished items found",
+ "lift_banishment": "Lift Banishment",
+ "lift_banishment_confirm": "Are you sure you want to lift the banishment for {name}?",
+ "banished_at": "Banished at",
+ "banished_until": "Banished until",
+ "reason": "Reason",
+ "type": "Type",
+ "user": "User",
+ "node": "Node",
+ "blocked_destinations": "Blocked destinations",
+ "banishment_lifted": "Banishment lifted successfully",
+ "failed_lift_banishment": "Failed to lift banishment",
+ "lift_selected": "Lift banishment ({count})",
+ "lift_selected_confirm": "Lift banishment for {count} selected items?",
+ "lift_selected_success": "Lifted banishment for {count} items",
+ "filter_all_types": "All types",
+ "filter_rns": "RNS blackhole",
+ "sort_newest": "Newest first",
+ "sort_oldest": "Oldest first",
+ "sort_name": "Name A-Z",
+ "result_count": "{count} items"
+ },
+ "nomadnet": {
+ "remove_favourite": "Remove Favourite",
+ "add_favourite": "Add Favourite",
+ "identify": "Identify",
+ "pop_out_browser": "Pop out browser",
+ "hide_source": "Hide source",
+ "new_tab": "New tab",
+ "new_tab_shortcut": "New tab (Ctrl+T)",
+ "page_archives": "Page Archives",
+ "archive_current_version": "Archive Current Version",
+ "no_archives_for_this_page": "No archives for this page",
+ "viewing_archived_version_from": "Viewing archived version from {time}",
+ "viewing_archived_version": "Viewing archived version",
+ "load_live": "Load Live",
+ "failed_to_load_page": "Failed to load page",
+ "archived_version_available": "An archived version of this page is available.",
+ "view_archive": "View Archive",
+ "no_active_node": "No Active Node",
+ "select_node_to_browse": "Select a Node to start browsing!",
+ "open_nomadnet_url": "Open a Nomadnet URL",
+ "unknown_node": "Unknown Node",
+ "existing_download_in_progress": "An existing download is in progress. Please wait for it to finish before starting another download.",
+ "favourites": "Favourites",
+ "announces": "Announces",
+ "search_favourites_placeholder": "Search {count} favourites...",
+ "search_placeholder_announces": "Search {count} recent announces...",
+ "rename": "Rename",
+ "remove": "Remove",
+ "no_favourites": "No favourites",
+ "add_nodes_from_announces": "Add nodes from the announces tab.",
+ "favourites_search_no_results": "No favourites match your search",
+ "favourites_search_try_other": "Try a different search term.",
+ "no_favourites_in_section": "No favourites in this section.",
+ "search_announces": "Search announces",
+ "searching_announces": "Searching announces...",
+ "announced_time_ago": "Announced {time} ago",
+ "block_node": "Banish Node",
+ "no_announces_yet": "No announces yet",
+ "no_search_results_peers": "No peers found matching your search.",
+ "listening_for_peers": "Listening for peers on the mesh.",
+ "block_node_confirm": "Are you sure you want to banish {name}? Their announces will be ignored and they won't appear in the announce stream.",
+ "node_blocked_successfully": "Node banished successfully",
+ "failed_to_block_node": "Failed to banish node",
+ "failed_rename_favourite": "Failed to rename favourite",
+ "rename_favourite": "Rename this favourite",
+ "remove_favourite_confirm": "Are you sure you want to remove this favourite?",
+ "enter_nomadnet_url": "Enter a Nomadnet URL",
+ "archiving_page": "Archiving page...",
+ "page_archived_successfully": "Page archived successfully.",
+ "identify_confirm": "Are you sure you want to identify yourself to this NomadNetwork Node? The page will reload after your identity has been sent.",
+ "banishment_lifted": "Banishment lifted successfully",
+ "failed_lift_banishment": "Failed to lift banishment",
+ "lift_banishment": "Lift Banishment",
+ "enter_section_name": "Enter section name",
+ "new_section": "New Section",
+ "rename_section": "Rename section",
+ "delete_section_confirm": "Delete this section? Favourites will move to the main section.",
+ "unsupported_url": "unsupported url: ",
+ "path_away_suffix": "away",
+ "renderer_chip_micron_js": "Micron JS",
+ "renderer_chip_micron_wasm": "Micron WASM",
+ "renderer_hint_micron_js": "micron-parser (JavaScript)",
+ "renderer_hint_micron_wasm": "micron-parser-go (WebAssembly)",
+ "renderer_hint_micron_wasm_pending": "Faster version loading or unavailable. Using the standard version.",
+ "renderer_popover_micron_wasm_powered": "Powered by ",
+ "renderer_popover_micron_wasm_active_tail": " {version}. WebAssembly renderer.",
+ "renderer_popover_micron_wasm_pending_tail": " {version} is loading or unavailable. Using the standard version.",
+ "renderer_switch_title": "Parser",
+ "renderer_menu_js": "Micron: JavaScript",
+ "renderer_menu_wasm": "Micron: WebAssembly",
+ "renderer_setting_failed": "Could not update Micron renderer",
+ "multiline_hint": "Press Enter again to expand to multi-line",
+ "renderer_chip_markdown": "Markdown",
+ "renderer_chip_html": "HTML",
+ "renderer_chip_plaintext": "Text",
+ "renderer_hint_markdown": "Markdown renderer",
+ "renderer_hint_html": "Sanitized HTML",
+ "renderer_hint_plaintext": "Escaped plain text",
+ "load_phase_default": "Loading page",
+ "load_phase_finding_path": "Looking for a network path to the node",
+ "load_phase_establishing_link": "Establishing link to the node",
+ "page_download_cancelled": "Download cancelled.",
+ "load_phase_requesting_page": "Sending page request",
+ "load_phase_transferring": "Receiving page",
+ "path_finder": "Path Finder",
+ "path_finder_quick_request": "Request path",
+ "path_finder_force_find": "Force find path (wait)",
+ "path_finder_drop_and_request": "Drop path and re-discover",
+ "path_finder_load_archive": "Load latest archive snapshot",
+ "path_finder_request_sent": "Path request sent",
+ "path_finder_found": "Path found, retrying page",
+ "path_finder_not_found": "No path found within timeout",
+ "path_finder_dropped_and_requested": "Path dropped, requesting again",
+ "path_finder_failed": "Path finder request failed",
+ "path_finder_archive_loading": "Looking for archive snapshot...",
+ "export_section_favourites": "Export section favourites",
+ "section_favourites_exported": "Section favourites exported",
+ "section_favourites_export_failed": "Could not export section favourites",
+ "sidebar_selection_mode": "Selection mode",
+ "bulk_selected_count": "{count} selected",
+ "bulk_remove": "Remove",
+ "bulk_move_to_section": "Move to section",
+ "bulk_remove_favourites_confirm": "Remove {count} favourites from this device?",
+ "bulk_remove_favourites_done": "Removed {count} favourites",
+ "bulk_block_confirm": "Banish {count} nodes from the announce list?",
+ "bulk_block_done": "Banished {count} nodes",
+ "bulk_add_to_favourites": "Add to favourites",
+ "bulk_add_favourites_done": "Added {count} nodes to favourites",
+ "bulk_block_nodes": "Banish",
+ "bulk_nothing_to_add_favourites": "Nothing to add (already favourites or none selected)",
+ "context_tabs": "Tabs",
+ "close_tabs_to_right": "Close tabs to the right",
+ "close_other_tabs": "Close other tabs",
+ "close_all_tabs": "Close all tabs",
+ "download_page": "Download page",
+ "download_page_started": "Page download started",
+ "download_page_unavailable": "No page content available to download",
+ "download_page_failed": "Could not download page",
+ "view_source_unavailable": "No page loaded to view source",
+ "context_menu_page_unavailable": "No active page for this tab",
+ "context_menu_action_failed": "Context menu action failed",
+ "context_menu_favourite_failed": "Could not update favourite",
+ "favourite_added": "Added to favourites",
+ "favourite_removed": "Removed from favourites",
+ "tab_switch_failed": "Could not switch to that tab",
+ "tab_content_mismatch": "This tab was out of sync and is being reloaded",
+ "tab_restore_failed": "Could not restore this tab's page",
+ "open_node_failed": "Could not open the NomadNet node"
+ },
+ "forwarder": {
+ "title": "LXMF Forwarder",
+ "description": "Forward LXMF between addresses with reply routing preserved.",
+ "add_rule": "Add Forwarding Rule",
+ "name": "Name",
+ "name_placeholder": "Rule name (e.g. Home Node)...",
+ "forward_to_hash": "Forward to Hash",
+ "destination_placeholder": "Destination LXMF hash...",
+ "source_filter": "Source Filter (Optional)",
+ "source_filter_placeholder": "Only forward from this hash...",
+ "add_button": "Add Rule",
+ "active_rules": "Active Rules",
+ "no_rules": "No forwarding rules configured.",
+ "active": "Active",
+ "disabled": "Disabled",
+ "forwarding_to": "Forwarding to: {hash}",
+ "source_filter_display": "Source filter: {hash}",
+ "delete_confirm": "Are you sure you want to delete this rule?",
+ "invalid_hash": "Forward-to hash must be a 32-character hex value",
+ "send_failed": "Could not send forwarding update (websocket disconnected)",
+ "rule_added": "Forwarding rule added",
+ "rule_deleted": "Forwarding rule deleted"
+ },
+ "archives": {
+ "description": "Search Nomad Network pages stored in your local archive.",
+ "search_placeholder": "Search content, hash or path...",
+ "loading": "Loading archives...",
+ "no_archives_found": "No archives found",
+ "adjust_filters": "Try adjusting your search filters.",
+ "browse_to_archive": "Archived pages will appear here once you browse Nomad Network sites.",
+ "page": "Page",
+ "pages": "Pages",
+ "view": "View",
+ "showing_range": "Showing {start} to {end} of {total} archives",
+ "page_of": "Page {page} of {total_pages}",
+ "export_mu": "Export .mu",
+ "export_selected_mu": "Export .mu ({count})",
+ "nodes_title": "Nodes",
+ "snapshots_title": "Snapshots",
+ "select_all": "Select all",
+ "delete_selected": "Delete ({count})",
+ "viewing_archive": "Viewing archive",
+ "select_snapshot": "Select a snapshot to view",
+ "open_live": "Open live",
+ "search_nodes_placeholder": "Search nodes or content...",
+ "no_results": "No results found",
+ "no_archives": "No archives yet",
+ "show_nodes": "Show nodes",
+ "hide_nodes": "Hide nodes",
+ "show_snapshots": "Show snapshots",
+ "hide_snapshots": "Hide snapshots",
+ "delete_snapshot": "Delete snapshot"
+ },
+ "docs": {
+ "title": "Documentation",
+ "subtitle": "Reticulum manual and MeshChatX guides, available offline after setup.",
+ "tab_meshchatx": "MeshChatX",
+ "tab_reticulum": "Reticulum",
+ "search_placeholder": "Search documentation...",
+ "search_placeholder_mobile": "Search all documentation...",
+ "search_results": "Search results",
+ "matches_count": "{count} matches",
+ "no_results": "No results found",
+ "no_results_hint": "Try different keywords or check spelling.",
+ "clear_search": "Clear search",
+ "versions": "Versions",
+ "no_versions": "No versions available",
+ "default_version": "Default",
+ "upload_zip": "Upload ZIP",
+ "open_external": "Open",
+ "dismiss": "Dismiss",
+ "sections_title": "Guides",
+ "on_this_page": "On this page",
+ "language_label": "Guide language",
+ "select_doc": "Select a guide from the sidebar",
+ "no_docs_found": "No MeshChatX guides found",
+ "no_docs_hint": "Guides are copied from the docs folder when the app starts.",
+ "reticulum_manual": "Reticulum manual",
+ "complete_percent": "{percent}% complete",
+ "confirm_delete_version": "Delete documentation version \"{version}\"?",
+ "prompt_version_name": "Enter a version name for this upload:",
+ "status_title": "Documentation Status",
+ "status_extracting": "Extracting Documentation...",
+ "status_available": "Offline Manual Available",
+ "status_not_available": "Manual Not Available",
+ "btn_upload": "Upload Manual",
+ "btn_share": "Share the Reticulum manual as a re-uploadable ZIP",
+ "error": "Error",
+ "failed_upload_docs": "Failed to upload documentation",
+ "upload_success": "Documentation uploaded",
+ "docs_link_copied": "Documentation link copied to clipboard",
+ "failed_copy_link": "Failed to copy link",
+ "load_list_failed": "Could not load the guide list. Try again in a moment.",
+ "load_doc_failed": "Could not load this guide.",
+ "search_failed": "Search failed. Check your connection and try again.",
+ "manifest_warning": "The documentation index file could not be read. Showing available files without section grouping.",
+ "failed_upload_alert": "Failed to upload documentation: {message}",
+ "empty_state_hint": "Upload a Reticulum manual ZIP to view it offline. Releases ship a bundled offline copy built at packaging time."
+ },
+ "licenses": {
+ "section_label": "Legal",
+ "title": "Third-party licenses",
+ "description": "Shows versions and licenses for all software components. This data is collected from the local environment.",
+ "short_description": "Dependency versions, authors, and licenses for backend and frontend.",
+ "search_placeholder": "Search packages, versions, authors, licenses...",
+ "generated_at": "Generated at {time}",
+ "frontend_source": "Frontend data: {source}",
+ "backend_section": "Backend (Python)",
+ "frontend_section": "Frontend (Node)",
+ "col_package": "Package",
+ "col_version": "Version",
+ "col_author": "Author",
+ "col_license": "License"
+ },
+ "tools": {
+ "utilities": "Utilities",
+ "power_tools": "Utilities",
+ "diagnostics_description": "Mesh and Reticulum utilities in one place: reachability, paths, file transfer, translation, documentation, and hardware helpers. Open any row for the full tool.",
+ "back_to_tools": "Back to tools",
+ "beta_badge": "Beta",
+ "alpha_badge": "Alpha",
+ "ping": {
+ "title": "Ping",
+ "description": "Measure round-trip time to another node or app that answers ping."
+ },
+ "rnprobe": {
+ "title": "RNProbe",
+ "description": "Send test payloads over the mesh to check reachability and delay."
+ },
+ "rncp": {
+ "title": "RNCP",
+ "description": "Send files to or fetch files from another destination over Reticulum, with progress and optional compression."
+ },
+ "rnsh": {
+ "title": "RNSH",
+ "description": "Manage persistent rnsh sessions"
+ },
+ "rnx": {
+ "title": "RNX",
+ "description": "Remote command execution over Reticulum"
+ },
+ "rnstatus": {
+ "title": "RNStatus",
+ "description": "Per-interface traffic counters and link status from your Reticulum instance."
+ },
+ "rnpath": {
+ "title": "RNPath",
+ "description": "Inspect paths and announce queues, and request or drop routes when you need to steer traffic.",
+ "failed_fetch": "Failed to fetch path data",
+ "path_dropped": "Path dropped",
+ "failed_drop": "Could not drop path",
+ "error_drop": "Error dropping path",
+ "failed_request": "Failed to request path",
+ "path_requested": "Path requested for {hash}...",
+ "drop_confirm": "Are you sure you want to drop the path to {hash}?",
+ "drop_via_confirm": "Drop ALL paths via {hash}?",
+ "invalid_hops": "Hops filter must be a number",
+ "paths_dropped": "Paths dropped",
+ "failed_drop_paths": "Failed to drop paths",
+ "purge_confirm": "Purge all announce queues? This cannot be undone.",
+ "queues_purged": "Announce queues purged",
+ "failed_purge": "Failed to purge queues"
+ },
+ "rnpath_trace": {
+ "title": "RNS Path Trace",
+ "description": "List each hop along the path to a destination hash.",
+ "destination_hash": "Destination Hash",
+ "placeholder": "Enter 32-char destination hash...",
+ "trace": "Trace Path",
+ "tracing": "Tracing path through the mesh...",
+ "total_hops": "Total Hops",
+ "interface": "Exit Interface",
+ "next_hop": "Next Hop",
+ "ready_title": "Trace a Path",
+ "ready_desc": "Enter a destination hash to list hops along the current path.",
+ "unknown_hops": "{count} Intermediate Hops",
+ "ping_test": "Test with Ping"
+ },
+ "translator": {
+ "title": "Translator",
+ "description": "Translate text with LibreTranslate over the network or Argos Translate on this device."
+ },
+ "forwarder": {
+ "title": "Forwarder",
+ "description": "Forward LXMF messages between addresses while keeping reply routing intact."
+ },
+ "propagation_nodes": {
+ "title": "Propagation nodes",
+ "description": "Pick preferred mesh propagation nodes, watch live stats, and run quick path checks.",
+ "load_failed": "Failed to load propagation nodes",
+ "local_restarted": "Local propagation node restarted",
+ "local_stopped": "Local propagation node stopped",
+ "local_started": "Local propagation node started"
+ },
+ "sieve_filters": {
+ "title": "Sieve filters",
+ "description": "Auto-sort mail: match words in peer names or message text, then hide, silence, move to a folder, or block the sender.",
+ "subtitle": "Rules run in order. Scope picks everyone, contacts only, or non-contacts only. Choose substring or regex matching. The first enabled rule whose scope fits and whose targets match wins.",
+ "back_tools": "Back to tools",
+ "rules_heading": "Rules",
+ "add_rule": "Add rule",
+ "order_hint": "Drag order is top to bottom: the first matching rule applies.",
+ "empty_rules": "No rules yet. Add a rule or save an empty list to clear filters.",
+ "enabled": "Enabled",
+ "move_up": "Move up",
+ "move_down": "Move down",
+ "remove_rule": "Remove rule",
+ "terms_label": "Terms or patterns",
+ "terms_placeholder": "One per line or separated by commas. Matches are case-insensitive. You can also use regular expressions (one per line).",
+ "match_targets_label": "Match in",
+ "match_peer_fields": "Peer display name, custom name, contact fields",
+ "match_message": "Message title and body (when available)",
+ "match_targets_hint": "At least one target must stay enabled.",
+ "match_mode_label": "Match style",
+ "match_mode_substring": "Substring",
+ "match_mode_regex": "Regular expression",
+ "scope_label": "Applies to",
+ "scope_everyone": "Everyone",
+ "scope_contacts": "Contacts only",
+ "scope_non_contacts": "Non-contacts only",
+ "graph_scope_everyone": "Everyone",
+ "graph_scope_contacts": "Contacts",
+ "graph_scope_non_contacts": "Non-contacts",
+ "graph_match_peer": "peer",
+ "graph_match_message": "msg",
+ "graph_match_mode_substring": "substring",
+ "graph_match_mode_regex": "regex",
+ "action_label": "Then",
+ "action_hide": "Hide conversation",
+ "action_ignore": "Ignore (no notifications)",
+ "action_folder": "Move to folder",
+ "action_banish": "Banish sender (full block)",
+ "folder_label": "Folder",
+ "save": "Save",
+ "saving": "Saving…",
+ "revert": "Reload",
+ "saved": "Sieve filters saved",
+ "save_failed": "Could not save sieve filters",
+ "load_failed": "Could not load sieve filters",
+ "flow_heading": "Rule flow",
+ "flow_source": "Peers",
+ "flow_source_hint": "Peer fields use announced display name, your custom name, and contact name or LXMF address.",
+ "flow_if": "If",
+ "flow_hide": "Hide",
+ "flow_banish": "Banish",
+ "flow_folder": "Folder",
+ "flow_no_rules": "No rules"
+ },
+ "message_blocklist": {
+ "title": "Message blocklist",
+ "description": "Auto-banish senders when their message or name matches a word or regex from your shared blocklist.",
+ "beta_notice_title": "Beta (work in progress)",
+ "beta_notice_body": "This tool is off until you turn it on. Words match regardless of case. Regex uses normal patterns. If something matches, the sender gets banished right away. Export a list to share with your group, or import one to merge or replace yours.",
+ "enable_label": "Enable message blocklist",
+ "enable_hint": "When disabled, entries are saved but no automatic banish runs.",
+ "entries_heading": "Blocked phrases",
+ "add_entry": "Add phrase",
+ "empty_entries": "No phrases yet. Add entries or import a shared list.",
+ "entry_enabled": "Active",
+ "entry_placeholder": "spam phrase or regex pattern",
+ "remove_entry": "Remove",
+ "scope_label": "Applies to",
+ "scope_everyone": "Everyone",
+ "scope_contacts": "Contacts only",
+ "scope_non_contacts": "Non-contacts only",
+ "match_in_label": "Match in",
+ "match_message": "Message title and body",
+ "match_peer_fields": "Peer display name and contact fields",
+ "match_mode_substring": "Word",
+ "match_mode_regex": "Regex",
+ "export": "Export",
+ "import": "Import",
+ "save": "Save",
+ "saving": "Saving…",
+ "revert": "Reload",
+ "saved": "Blocklist saved",
+ "save_failed": "Could not save blocklist",
+ "load_failed": "Could not load blocklist",
+ "enabled_toast": "Message blocklist enabled",
+ "disabled_toast": "Message blocklist disabled",
+ "exported": "Blocklist exported",
+ "export_failed": "Could not export blocklist",
+ "import_merge_confirm": "Merge with your current list? Choose Cancel to replace your list entirely.",
+ "imported_merge": "Blocklist merged",
+ "imported_replace": "Blocklist replaced",
+ "import_failed": "Could not import blocklist"
+ },
+ "repository_server": {
+ "title": "Repository server",
+ "description": "Redistribute MeshChatX and Reticulum-related Python wheels plus your own files over plain HTTP so people on your network, or on this device, can download them.",
+ "subtitle": "Refresh the included software packages, add your own files, or start a download page for other devices.",
+ "back_tools": "Back to tools",
+ "http_heading": "Plain HTTP mirror",
+ "host_label": "Listen address",
+ "port_label": "Port",
+ "start_http": "Start",
+ "stop_http": "Stop",
+ "restart_http": "Restart",
+ "http_listen_label": "Listening:",
+ "open_http": "Open in browser",
+ "http_started": "HTTP server started",
+ "http_stopped": "HTTP server stopped",
+ "http_restarted": "HTTP server restarted",
+ "http_err_generic": "HTTP server request failed",
+ "http_err_invalid_host": "Invalid listen address",
+ "http_err_invalid_port": "Invalid port",
+ "http_err_already_running": "HTTP server is already running",
+ "http_err_bind_failed": "Could not bind (port in use or permission denied)",
+ "upload_heading": "Upload file",
+ "choose_file": "Choose file",
+ "refresh_bundled_tooltip": "Download bundled wheels to this device (needs network)",
+ "refresh_started": "Downloading bundled packages…",
+ "refresh_fetching": "Getting {pkg}…",
+ "refresh_ok": "Bundled wheels refreshed",
+ "refresh_ok_count": "{n} packages downloaded",
+ "refresh_failed": "Could not refresh bundled wheels",
+ "refresh_pip_missing": "pip was not found on this system. Install pip and try again.",
+ "refresh_partial": "Some packages could not be downloaded (details below)",
+ "refresh_partial_toast": "Bundled download finished with some failures",
+ "files_heading": "Files",
+ "col_name": "Name",
+ "col_source": "Source",
+ "col_size": "Size",
+ "empty": "No files yet. Upload a file or refresh bundled wheels.",
+ "delete": "Delete",
+ "delete_confirm": "Delete uploaded file \"{name}\"?",
+ "delete_ok": "File removed",
+ "delete_failed": "Could not delete file",
+ "upload_ok": "File uploaded",
+ "upload_failed": "Upload failed",
+ "load_failed": "Could not load repository"
+ },
+ "rnode_flasher": {
+ "title": "RNode Flasher",
+ "description": "Flash or update RNode firmware from the browser when your hardware and browser support USB or Wi‑Fi flashing.",
+ "select_device": "1. Select your device",
+ "product": "Product",
+ "model": "Model",
+ "connection_method": "Connection Method",
+ "serial": "Serial",
+ "wifi": "WiFi",
+ "ip_address": "IP Address",
+ "ip_address_placeholder": "e.g. 192.168.1.100",
+ "select_product": "Select a Product",
+ "select_model": "Select a Model",
+ "enter_dfu_mode": "Enter DFU Mode",
+ "entering_dfu_mode": "Entering DFU Mode...",
+ "find_device_issue": "Can't find your device? Open an issue on",
+ "select_firmware": "2. Select firmware to flash (.zip)",
+ "flash_now": "Flash Now",
+ "flashing": "Flashing: {percentage}%",
+ "flashing_file_progress": "File {current}/{total}: {percentage}%",
+ "connecting_device": "Connecting to device...",
+ "download_firmware": "Download Firmware",
+ "official_firmware": "Official Firmware",
+ "ce_firmware": "CE Firmware",
+ "transport_node_firmware": "Transport Node Firmware",
+ "common_issues": "Common Issues",
+ "hardware_failure": "Hardware Failure:",
+ "provision_eeprom_hint": "You need to provision the eeprom in step 3.",
+ "firmware_corrupt": "Firmware Corrupt:",
+ "set_firmware_hash_hint": "You need to set the firmware hash in step 4.",
+ "step_provision": "3. Provision EEPROM",
+ "provision_description": "Sets device info, checksum and blank signature",
+ "provision": "Provision",
+ "provisioning_wait": "Provisioning: please wait...",
+ "step_set_hash": "4. Set Firmware Hash",
+ "set_hash_description": "Uses hash from board",
+ "set_firmware_hash": "Set Firmware Hash",
+ "setting_hash_wait": "Setting Firmware Hash: please wait...",
+ "step_done": "5. Finalize",
+ "download_recommended": "Download & Load Recommended Firmware",
+ "downloading": "Downloading...",
+ "done_description": "If you made it this far, and all previous steps were successful, your RNode should be ready to use.",
+ "meshchat_usage": "To use RNode with MeshChat, you will need to add an RNodeInterface in the Interfaces → Add Interface page.",
+ "sideband_usage": "To use RNode with Sideband, you will need to configure it in Hardware → RNode and enable Connectivity → Connect via RNode.",
+ "restart_warning": "You must restart MeshChat and Sideband for interface setting changes to take effect, otherwise nothing will happen!",
+ "advanced_tools": "Advanced Tools",
+ "detect_rnode": "Detect RNode",
+ "reboot_rnode": "Reboot RNode",
+ "read_display": "Read Display",
+ "dump_eeprom": "Dump EEPROM",
+ "wipe_eeprom": "Wipe EEPROM",
+ "eeprom_console_hint": "EEPROM dumps are shown in dev tools console.",
+ "configure_bluetooth": "Configure Bluetooth (optional)",
+ "bluetooth_info_1": "Bluetooth is not supported on all devices.",
+ "bluetooth_info_2": "Some devices use Bluetooth Classic, and some use BLE (Bluetooth Low Energy)",
+ "bluetooth_info_3": "Put the RNode into Bluetooth Pairing mode, then connect to it from Android Bluetooth settings.",
+ "bluetooth_info_4": "Once you have initiated a pair request from Android, a PIN should show on the RNode display.",
+ "bluetooth_info_5": "In Sideband you will need to enable Connect using Bluetooth in Hardware → RNode.",
+ "bluetooth_info_6": "If your device uses BLE you will also need to enable Device requires BLE in Hardware → RNode.",
+ "bluetooth_restart_warning": "Don't forget to restart Sideband for the setting changes to take effect!",
+ "enable": "Enable",
+ "disable": "Disable",
+ "start_pairing": "Start Pairing",
+ "configure_tnc": "Configure TNC Mode (optional)",
+ "tnc_info_1": "TNC mode allows an RNode to be used as a KISS compatible TNC over the Serial Port.",
+ "tnc_info_2": "This mode makes it usable with amateur radio software that can talk to a KISS TNC over a serial port.",
+ "tnc_warning": "You must leave TNC mode disabled when using RNode with apps like MeshChat or Sideband.",
+ "frequency": "Frequency (Hz)",
+ "bandwidth": "Bandwidth",
+ "tx_power": "Tx Power (dBm)",
+ "spreading_factor": "Spreading Factor",
+ "coding_rate": "Coding Rate",
+ "configure_display": "Configure Display",
+ "rotation": "Rotation",
+ "reconditioning": "Reconditioning",
+ "start": "Start",
+ "stop": "Stop",
+ "rotation_v180_warning": "Setting display rotation requires firmware v1.80+",
+ "errors": {
+ "failed_download": "Failed to download firmware: {error}",
+ "firmware_not_found_in_release": "Recommended firmware not found in the latest release.",
+ "web_serial_not_supported": "Web Serial is not supported in this browser. Please use Chrome, Edge, or another Chromium-based browser.",
+ "no_device_selected": "No device selected. Please select a serial port.",
+ "failed_connect": "Failed to connect to device: {error}",
+ "not_an_rnode": "Selected device is not an RNode! Please make sure you've selected the correct serial port.",
+ "failed_dfu": "Failed to enter DFU mode: {error}",
+ "select_firmware_first": "Please select a firmware file (.zip) first.",
+ "failed_flash": "Firmware flashing failed: {error}",
+ "failed_ota": "OTA flashing failed: {error}",
+ "esptool_not_loaded": "esptool-js could not be loaded. Please refresh the page and try again.",
+ "no_flash_config": "Flash configuration is not available for the selected device. Please select a different device or check the firmware file.",
+ "failed_extract": "Required file \"{file}\" not found in firmware ZIP file. Please download the correct firmware for your device.",
+ "failed_detect": "Failed to detect RNode. Make sure the device is connected and in the correct mode.",
+ "provisioned_already": "Eeprom is already provisioned. You must wipe it to reprovision!",
+ "select_product_first": "Please select a product!",
+ "select_model_first": "Please select a model!",
+ "not_provisioned": "Eeprom is not provisioned. You must do this first!",
+ "failed_provision": "failed to provision: {error}",
+ "failed_set_hash": "failed to set firmware hash: {error}",
+ "failed_reboot": "Failed to reboot RNode: {error}",
+ "failed_read_display": "Failed to read display: {error}",
+ "failed_dump_eeprom": "Failed to dump EEPROM: {error}",
+ "failed_wipe_eeprom": "Failed to wipe EEPROM: {error}",
+ "failed_enable_tnc": "Failed to enable TNC mode: {error}",
+ "failed_disable_tnc": "Failed to disable TNC mode: {error}",
+ "failed_enable_bluetooth": "Failed to enable Bluetooth: {error}",
+ "failed_disable_bluetooth": "Failed to disable Bluetooth: {error}",
+ "failed_start_pairing": "Failed to start pairing: {error}",
+ "failed_set_rotation": "Failed to set display rotation: {error}",
+ "failed_start_reconditioning": "Failed to start display reconditioning: {error}",
+ "no_main_bin": "Could not locate the main firmware binary in the ZIP.",
+ "invalid_host": "Please enter a valid host or IP address.",
+ "bluetooth_flash_unsupported": "Bluetooth cannot be used to flash firmware. Use Serial or WiFi to flash, then return to Bluetooth for management.",
+ "failed_diagnostics": "Failed to run diagnostics: {error}",
+ "failed_detect_with_reason": "Failed to detect RNode: {error}"
+ },
+ "alerts": {
+ "firmware_downloaded": "Recommended firmware has been downloaded and loaded.",
+ "dfu_ready": "Device should now be in DFU mode. You can now proceed with flashing firmware.",
+ "flash_success": "Firmware has been flashed successfully!",
+ "rebooting": "Board is rebooting!",
+ "eeprom_wipe_confirm": "Are you sure you want to wipe the eeprom on this device? This will take about 30 seconds. An alert will show when the eeprom wipe has finished.",
+ "eeprom_wiped": "eeprom has been wiped!",
+ "eeprom_dumped": "EEPROM dumped to console",
+ "rnode_detected": "RNode v{version} detected",
+ "provision_success": "device has been provisioned!",
+ "hash_success": "firmware hash has been set!",
+ "tnc_enabled": "TNC mode has been enabled!",
+ "tnc_disabled": "TNC mode has been disabled!",
+ "bluetooth_enabled": "Bluetooth has been enabled!",
+ "bluetooth_disabled": "Bluetooth has been disabled!",
+ "bluetooth_pairing_pin": "Bluetooth Pairing Pin: {pin}",
+ "bluetooth_pairing_started": "Pairing mode started (30s)",
+ "rotation_updated": "Rotation updated",
+ "reconditioning_started": "Reconditioning started",
+ "diagnostics_healthy": "Diagnostics: device looks healthy.",
+ "diagnostics_issues": "Diagnostics: {count} issue(s) detected."
+ },
+ "simple": "Simple",
+ "advanced": "Advanced",
+ "original": "Original",
+ "open_original_tab": "Open the original flasher in a new tab",
+ "bluetooth": "Bluetooth",
+ "preparing_firmware": "Preparing firmware...",
+ "uploading": "Uploading: {percentage}%",
+ "select_firmware_file": "Or select a firmware file (.zip)",
+ "wifi_help": "The IP address of your RNode on the local network.",
+ "run_diagnostics": "Diagnostics",
+ "support": {
+ "serial": {
+ "title": "USB serial is unavailable",
+ "polyfill_not_loaded": "USB serial polyfill has not been loaded yet.",
+ "android_webview_no_serial": "Android does not support Web Serial in this view. Use a desktop browser or the built-in flasher.",
+ "browser_unsupported": "This browser does not support Web Serial. Try Chrome, Edge, or another Chromium-based browser.",
+ "android_use_chrome": "On Android, opening this page in Chrome lets you use Web Serial via WebUSB.",
+ "unknown": "USB serial cannot be initialised in this environment."
+ },
+ "bluetooth": {
+ "title": "Bluetooth is unavailable",
+ "android_bridge_not_implemented": "Web Bluetooth is not available here. Use the OS Bluetooth settings or a different browser.",
+ "browser_unsupported": "This browser does not support Web Bluetooth. Try Chrome or Edge.",
+ "insecure_context": "Web Bluetooth requires a secure context (HTTPS or localhost).",
+ "requires_https": "Open the flasher over HTTPS or via localhost to enable Bluetooth.",
+ "unknown": "Bluetooth cannot be initialised in this environment."
+ },
+ "actions": {
+ "load_polyfill": "Load polyfill",
+ "request_bluetooth": "Allow Bluetooth",
+ "open_settings": "Open settings",
+ "polyfill_loading": "Loading USB serial polyfill...",
+ "bluetooth_requested": "Bluetooth permission requested."
+ }
+ },
+ "diagnostics": {
+ "title": "Device Diagnostics",
+ "healthy": "Healthy",
+ "needs_attention": "Needs attention",
+ "issues_detected": "Issues detected",
+ "firmware_version": "Firmware",
+ "platform": "Platform",
+ "board": "Board",
+ "provisioned": "Provisioned",
+ "product": "Product",
+ "model": "Model",
+ "firmware_hash": "FW hash",
+ "target_hash": "Target hash",
+ "suggestions": {
+ "not_provisioned": "EEPROM is not provisioned. Run step 3 (Provision EEPROM).",
+ "firmware_hash_missing": "Firmware hash is missing. Run step 4 (Set Firmware Hash).",
+ "firmware_hash_mismatch": "Firmware hash mismatch. Re-flash the firmware and re-run step 4.",
+ "product_mismatch": "Selected product does not match the connected device.",
+ "no_firmware_version": "Could not read firmware version. Try reconnecting the device.",
+ "detect_failed": "Could not detect this RNode. Verify cable, port, and power.",
+ "read_timeout": "Device did not respond in time. Try reconnecting or rebooting it."
+ }
+ }
+ },
+ "micron_editor": {
+ "title": "Micron Editor",
+ "description": "Write Nomad Network Micron pages with a live preview.",
+ "editor": "Editor",
+ "preview": "Preview",
+ "save": "Save",
+ "edit": "Edit",
+ "view_preview": "View Preview",
+ "placeholder": "Enter your Micron markup here...",
+ "reset": "Reset",
+ "new_tab": "New Tab",
+ "rename_tab": "Rename Tab",
+ "confirm_reset": "Are you sure you want to reset the editor? All your tabs and content will be lost.",
+ "confirm_delete_tab": "Are you sure you want to delete this tab?",
+ "main_tab": "Main",
+ "guide_tab": "NomadNet Guide",
+ "parser_by": "Using Micron Parser by",
+ "wasm_active": "WASM engine active (fast parser)",
+ "wasm_inactive": "JS engine active (slow parser, click for WASM)",
+ "publish_prompt_name": "index.mu already exists on \"{server}\". Enter a page name (without .mu):",
+ "publish_published": "Published \"{page}\" to {server}",
+ "publish_failed": "Failed to publish page",
+ "publish_all_prompt_server": "Enter server name to publish all tabs to:\n\nAvailable: {servers}",
+ "publish_server_not_found": "Server not found: {server}",
+ "publish_all_done": "Published {published}/{total} tabs to {server}"
+ },
+ "reticulum_config_editor": {
+ "title": "Reticulum Config Editor",
+ "description": "Edit the main Reticulum (RNS) configuration file inside MeshChatX. Restart RNS to apply changes.",
+ "info": "This is the raw Reticulum config file. Saving will not apply changes until RNS is restarted.",
+ "loading": "Loading config...",
+ "reload": "Reload",
+ "save": "Save",
+ "saving": "Saving...",
+ "saved": "Reticulum config saved",
+ "discard": "Discard",
+ "unsaved": "Unsaved changes",
+ "restore_defaults": "Restore defaults",
+ "restoring": "Restoring defaults...",
+ "restored": "Reticulum config restored to defaults",
+ "confirm_restore": "Restore the Reticulum config to defaults? Your current config will be overwritten.",
+ "confirm_leave": "You have unsaved changes. Leave anyway?",
+ "restart_required": "Restart required",
+ "restart_description": "Reticulum must be restarted for your config changes to take effect.",
+ "restart_now": "Restart RNS now",
+ "restart_done": "Reticulum restarted",
+ "failed_load": "Failed to load Reticulum config",
+ "failed_save": "Failed to save Reticulum config",
+ "failed_restore": "Failed to restore Reticulum config",
+ "failed_restart": "Failed to restart Reticulum",
+ "tips_title": "Tips",
+ "tip_sections": "The config must contain [reticulum] and [interfaces] sections.",
+ "tip_restart": "Use the Restart RNS button or the Interfaces page to apply changes.",
+ "tip_mobile": "Useful whenever you prefer not to edit the file in another program on disk."
+ },
+ "paper_message": {
+ "title": "Paper Message",
+ "description": "Encode LXMF messages as QR codes or scan codes back into the app."
+ },
+ "mesh_server": {
+ "title": "Mesh Server",
+ "description": "Publish Micron pages and small files for mesh peers to open."
+ },
+ "rns_tunnel": {
+ "title": "RNS Tunnel",
+ "description": "Carry IP traffic over Reticulum links (in development)."
+ },
+ "rns_filesync": {
+ "title": "File Sync",
+ "description": "Keep a shared folder in sync with mesh friends, like Syncthing over Reticulum."
+ },
+ "bots": {
+ "title": "LXMFy Bots",
+ "description": "Run simple LXMF bots (echo, notes, reminders) when the LXMFy add-on is installed."
+ }
+ },
+ "ping": {
+ "title": "Ping Mesh Peers",
+ "description": "Only destinations that expose the ping app type will answer.",
+ "destination_hash": "Destination Hash",
+ "timeout_seconds": "Ping Timeout (seconds)",
+ "start_ping": "Start Ping",
+ "stop": "Stop",
+ "clear_results": "Clear Results",
+ "drop_path": "Drop Path",
+ "status": "Status",
+ "running": "Running",
+ "idle": "Idle",
+ "last_rtt": "Last RTT",
+ "last_error": "Last Error",
+ "console_output": "Console Output",
+ "streaming_responses": "Streaming seq responses in real time",
+ "no_pings_yet": "No pings yet. Start a run to collect RTT data.",
+ "invalid_hash": "Invalid destination hash!",
+ "timeout_must_be_number": "Timeout must be a number!"
+ },
+ "relay_chat": {
+ "title": "Relay Chat",
+ "subtitle": "Reticulum Relay Chat hubs and rooms",
+ "hubs": "Hubs",
+ "add_hub": "Add Hub",
+ "add_hub_title": "Add a Relay Chat Hub",
+ "hub_hash": "Hub Destination Hash",
+ "hub_hash_placeholder": "Enter the hub identity hash",
+ "hub_name": "Hub Name (optional)",
+ "hub_name_placeholder": "My favourite hub",
+ "dest_name": "Destination Aspect (optional)",
+ "advanced": "Advanced",
+ "no_hubs": "No hubs configured yet. Add one to get started.",
+ "no_rooms": "No rooms joined. Join a room to start chatting.",
+ "available_rooms": "Available Rooms",
+ "join": "Join",
+ "no_room_selected": "Select a room to view messages.",
+ "load_previous": "Load earlier messages",
+ "rooms": "Rooms",
+ "members": "Members",
+ "join_room": "Join Room",
+ "join_room_placeholder": "room name",
+ "join_room_key_placeholder": "Room key (if required)",
+ "room_key_prompt": "Enter the key for #{room}",
+ "bad_key": "Wrong room key. Enter the correct key to join.",
+ "join_requested": "Join request sent",
+ "leave_room": "Leave Room",
+ "clear_messages": "Clear Messages",
+ "connect": "Connect",
+ "disconnect": "Disconnect",
+ "remove_hub": "Remove Hub",
+ "remove_hub_confirm": "Remove this hub and all of its local history?",
+ "leave_room_confirm": "Leave this room?",
+ "clear_messages_confirm": "Clear all messages in this room?",
+ "message_placeholder": "Write a message...",
+ "send": "Send",
+ "settings": "Hub Settings",
+ "auto_reconnect": "Auto reconnect",
+ "auto_list": "Auto list rooms",
+ "auto_who": "Auto list members",
+ "nickname": "Nickname override",
+ "nickname_placeholder": "Leave blank to use display name",
+ "save": "Save",
+ "status_disconnected": "Disconnected",
+ "status_connecting": "Connecting",
+ "status_connected": "Connected",
+ "status_failed": "Failed",
+ "motd": "Message of the day",
+ "system": "System",
+ "hub_added": "Hub added",
+ "hub_removed": "Hub removed",
+ "settings_saved": "Settings saved",
+ "messages_cleared": "Messages cleared",
+ "joined_room": "Joined room",
+ "left_room": "Left room",
+ "invalid_hub_hash": "Enter a valid hub hash",
+ "send_failed": "Failed to send message",
+ "action_failed": "Action failed",
+ "room_required": "A room name is required",
+ "tab_connect": "Connect",
+ "tab_host": "Host",
+ "host_title": "Hosted Hubs",
+ "host_subtitle": "Run your own Relay Chat hubs and create rooms others can join.",
+ "create_hub": "Create Hub",
+ "create_hub_title": "Create a Relay Chat Hub",
+ "no_hosted_hubs": "No hosted hubs yet. Create one to start relaying chat.",
+ "copy_hash": "Copy destination hash",
+ "hash_copied": "Destination hash copied",
+ "share_hub": "Share hub link",
+ "share_copied": "Relay share link copied",
+ "host_join_as_client": "Join this hub as a client",
+ "host_leave_as_client": "Leave this hub as a client",
+ "host_joined_as_client": "Joined hosted hub as client",
+ "host_start": "Start",
+ "host_status_running": "Running",
+ "host_status_stopped": "Stopped",
+ "host_hub_not_running": "Start the hub before managing rooms or members.",
+ "host_stop": "Stop",
+ "host_announce": "Announce now",
+ "host_announced": "Announced",
+ "host_delete": "Delete hub",
+ "host_clients": "clients",
+ "host_rooms": "rooms",
+ "host_private": "private",
+ "host_delete_room": "Delete room",
+ "host_no_rooms": "No rooms yet.",
+ "host_room_name": "room name",
+ "host_room_topic": "topic (optional)",
+ "host_add_room": "Add Room",
+ "host_greeting": "Greeting (optional)",
+ "host_greeting_placeholder": "Welcome message shown on connect",
+ "host_announce_periodically": "Announce on the network",
+ "host_hub_created": "Hub created",
+ "host_delete_hub_confirm": "Delete this hosted hub? Connected clients will be disconnected.",
+ "host_hub_deleted": "Hub deleted",
+ "host_room_created": "Room created",
+ "host_room_key_placeholder": "Room key (optional)",
+ "host_room_keyed": "This room requires a key",
+ "host_room_unkeyed": "This room has no key",
+ "host_set_room_key": "Set key",
+ "host_change_room_key": "Change key",
+ "host_clear_room_key": "Clear key",
+ "host_room_key_prompt": "Enter a new key for #{room}",
+ "host_room_key_required": "A room key is required",
+ "host_room_key_saved": "Room key saved",
+ "host_clear_room_key_confirm": "Remove the key from this room? Anyone will be able to join.",
+ "host_room_key_cleared": "Room key cleared",
+ "host_delete_room_confirm": "Delete this room?",
+ "host_room_deleted": "Room deleted",
+ "host_members_all_title": "Connected clients: {hub}",
+ "host_members_room_title": "#{room}: {hub}",
+ "host_member_rooms": "Rooms",
+ "host_members_search": "Search members...",
+ "host_rooms_search": "Search rooms...",
+ "host_rooms_search_empty": "No rooms match your search.",
+ "host_moderation_uptime": "{time} uptime",
+ "host_moderation_members": "{count} members",
+ "host_members_select": "Select a member to view messages and moderate",
+ "host_no_messages": "No messages from this user in the log yet",
+ "host_ban_hub": "Ban from hub",
+ "host_kick_confirm": "Kick {name} from #{room}?",
+ "host_kick_pick_room": "Kick {name} from which room? Enter one of: {rooms}",
+ "host_kick_no_room": "This user is not in any room, so they cannot be kicked.",
+ "host_kick_room_invalid": "Room not found. Choose one of the listed rooms.",
+ "host_cannot_moderate_self": "You cannot moderate your own connection.",
+ "host_ban_confirm": "Ban {name} from this hub?",
+ "host_room_ban_confirm": "Ban {name} from #{room}?",
+ "host_manage_rooms": "Manage rooms",
+ "host_moderate": "Moderate hub",
+ "host_moderation_title": "Moderation",
+ "host_moderation_title_hub": "Moderation: {hub}",
+ "host_moderation_title_room": "Moderation: {hub} (#{room})",
+ "host_moderation_tab_rooms": "Rooms",
+ "host_moderation_tab_members": "Members",
+ "host_moderation_hub_missing": "Hub not found.",
+ "host_rooms_modal_title": "Rooms: {hub}",
+ "host_rooms_select": "Select a room to view activity",
+ "host_room_activity": "Recent activity",
+ "host_no_activity": "No recent activity in this room",
+ "hub_icon": "Hub icon",
+ "hub_icon_choose": "Choose icon",
+ "hub_icon_picker_title": "Choose hub icon",
+ "hub_icon_search": "Search {count} icons...",
+ "hub_icon_no_results": "No icons match your search.",
+ "hub_icon_search_limit": "Showing first {count} results. Refine your search.",
+ "hub_icon_reset_default": "Use default icon",
+ "disabled_message": "Relay Chat is turned off in Settings. Enable it under Appearance to use hubs and hosting.",
+ "ctx_add_hub": "Add hub",
+ "ctx_add_room": "Join room",
+ "ctx_connect_hub": "Connect",
+ "ctx_disconnect_hub": "Disconnect",
+ "ctx_hub_settings": "Hub settings",
+ "ctx_copy_hub_address": "Copy hub address",
+ "ctx_share_hub": "Share hub",
+ "ctx_share_room": "Share room",
+ "ctx_remove_hub": "Remove hub",
+ "ctx_leave_room": "Leave room",
+ "ctx_reply_quote": "Reply with quote",
+ "ctx_mention_user": "Mention user",
+ "ctx_copy_message": "Copy message",
+ "ctx_kick_user": "Kick user",
+ "ctx_ban_user": "Ban user",
+ "tab_discovery": "Discover",
+ "discovery_title": "Discovered Hubs",
+ "discovery_subtitle": "Relay Chat hubs heard announcing on the network.",
+ "discovery_empty": "No hubs discovered yet. They appear here as they announce.",
+ "discovery_search": "Search {count} hubs",
+ "discovery_add": "Add",
+ "discovery_added": "Hub added from discovery",
+ "discovery_open": "Open",
+ "discovery_browse": "Browse discovered hubs",
+ "announced_ago": "Announced {time} ago",
+ "hops_away": "{count} hops",
+ "back": "Back",
+ "refresh": "Refresh",
+ "members_title": "Members",
+ "members_online": "Online",
+ "members_offline": "Offline",
+ "no_members": "No one here yet",
+ "show_members": "Show members",
+ "hide_members": "Hide members",
+ "discovery_refreshed": "Discovery refreshed",
+ "collapse_sidebar": "Collapse sidebar",
+ "expand_sidebar": "Expand sidebar",
+ "add_hub_card": "Add a hub",
+ "hub_display_name": "Display name",
+ "revert_hub_name": "Revert to announced name",
+ "search_messages": "Search",
+ "search_messages_placeholder": "Search (AND, OR, DATE:2026-06-02)...",
+ "search_empty_hint": "Search this room",
+ "search_syntax_hint": "Examples: hello world · alice OR bob · DATE:today · DATE:2026-06-02",
+ "search_no_results": "No messages match your search",
+ "members_search_placeholder": "Search members...",
+ "members_search_no_results": "No members match your search.",
+ "popout_channel": "Open in new window",
+ "new_message_toast": "New message in #{room}",
+ "host_users": "users",
+ "host_announce_interval": "Announce interval (minutes)",
+ "host_announce_interval_hint": "Every {minutes} minutes",
+ "host_hub_settings": "Hub settings",
+ "presence_joined": "{count} joined",
+ "presence_left": "{count} left",
+ "presence_events": "{count} join/leave events",
+ "presence_connection": "{count} connection events"
+ },
+ "rncp": {
+ "file_transfer": "File Transfer",
+ "title": "RNCP - Reticulum Network Copy",
+ "description": "Send, listen, or fetch files over Reticulum using RNCP (chunked transfers, optional compression).",
+ "usage_steps": "How to use RNCP:",
+ "step_1": "1. To **receive** a file, go to the **Listen** tab, enter allowed identity hashes (who can send to you), and click **Start Listening**.",
+ "step_2": "2. To **send** a file, get the **Destination Hash** of a listening node, enter the **Local File Path**, and click **Send File**.",
+ "step_3": "3. To **fetch** a file, get the **Destination Hash** of a node listening with 'Allow Fetch' enabled, enter the **Remote File Path**, and click **Fetch File**.",
+ "send_file": "Send File",
- "fetch_file": "Fetch File",
- "listen": "Listen",
- "destination_hash": "Destination Hash",
- "file_path": "File Path",
- "timeout_seconds": "Timeout (seconds)",
- "disable_compression": "Disable compression",
- "cancel": "Cancel",
- "progress": "Progress",
- "invalid_hash": "Invalid destination hash!",
- "provide_file_path": "Please provide a file path!",
- "file_sent_successfully": "File sent successfully. Transfer ID: {id}",
- "failed_to_send": "Failed to send file",
- "remote_file_path": "Remote File Path",
- "save_path_optional": "Save Path (optional)",
- "save_path_placeholder": "Leave empty for current directory",
- "allow_overwrite": "Allow overwrite",
- "provide_remote_file_path": "Please provide a remote file path!",
- "file_fetched_successfully": "File fetched successfully. Saved to: {path}",
- "failed_to_fetch": "Failed to fetch file",
- "allowed_hashes": "Allowed Identity Hashes (one per line)",
- "fetch_jail_path": "Fetch Jail Path (optional)",
- "allow_fetch": "Allow fetch",
- "start_listening": "Start Listening",
- "stop_listening": "Stop Listening",
- "listening_on": "Listening on:",
- "provide_allowed_hash": "Please provide at least one allowed identity hash!",
- "failed_to_start_listener": "Failed to start listener",
- "failed_to_stop_listener": "Failed to stop listener",
- "browse_file": "Browse",
- "browse_folder": "Choose folder",
- "receive_folder": "Received files folder",
- "open_folder": "Open folder",
- "show_in_folder": "Show in folder",
- "received_file": "File received",
- "receive_failed": "Receive failed",
- "send_complete": "Send complete",
- "fetch_complete": "Fetch complete",
- "transfer_status": "Status",
- "listening_active_background": "Listening continues in the background. Return here to manage or stop.",
- "web_path_hint": "Browsers cannot expose full file paths. Use the desktop app to browse, or paste the full path to the file on this machine.",
- "web_save_path_prompt": "Enter the absolute folder path on the machine running MeshChat (server) where the fetched file should be saved:"
- },
- "rns_filesync": {
- "eyebrow": "Shared folders",
- "title": "File Sync",
- "description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
- "usage_steps": "Quick start",
- "step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
- "step_2": "2. On Devices, paste their sync ID to connect. Manage local files under Files, or pull from Remote.",
- "step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
- "tab_folder": "Folder",
- "tab_devices": "Devices",
- "tab_files": "Files",
- "tab_remote": "Remote",
- "tab_sharing": "Sharing",
- "tab_status": "Folder",
- "tab_peers": "Devices",
- "tab_browse": "Remote",
- "tab_acl": "Sharing",
- "sync_directory": "Folder to sync",
- "sync_directory_placeholder": "Choose a folder under this identity",
- "sync_directory_help": "Folders stay inside this identity's MeshChat storage. Use Browse on any device to pick or create one.",
- "announce_interval": "Announce every (seconds)",
- "announce_interval_help": "How often others are reminded you are available to sync.",
- "monitor": "Watch folder for changes",
- "start": "Start syncing",
- "stop": "Stop syncing",
- "announce": "Announce now",
- "refresh": "Refresh",
- "running": "Running",
- "status_syncing": "Syncing",
- "status_stopped": "Stopped",
- "yes": "Yes",
- "no": "No",
- "peers_count": "Devices",
- "files_count": "Files",
- "destination_hash": "Sync ID",
- "share_id": "Your sync ID",
- "share_id_help": "Send this to friends so they can connect. Tap to copy.",
- "last_progress": "Latest transfer",
- "peer_hash_placeholder": "Friend's sync ID",
- "connect": "Connect",
- "disconnect": "Remove",
- "no_peers": "No devices connected yet. Start syncing, then paste a friend’s sync ID.",
- "no_files": "This folder is empty so far. Drop files in, or pull some from Remote.",
- "manager_help": "Browse and manage files in your sync folder. Uploads and deletes stay inside this folder only.",
- "manager_root": "(sync folder root)",
- "manager_loading": "Loading files...",
- "manager_empty": "This folder is empty. Upload a file or create a subfolder.",
- "upload": "Upload",
- "upload_done": "File uploaded",
- "download_local": "Download",
- "download_local_done": "Download started",
- "delete": "Delete",
- "delete_confirm": "Delete \"{name}\" from the sync folder?",
- "delete_done": "Deleted",
- "select_peer": "Choose a device",
- "browse": "List files",
- "no_remote_files": "No remote files yet. Connect a device, then list their files.",
- "download": "Get file",
- "acl_enforce": "Only allow listed devices (deny everyone else)",
- "acl_grant": "Allow device",
- "acl_updated": "Sharing rules updated",
- "perm_read": "Read",
- "perm_write": "Write",
- "perm_delete": "Delete",
- "no_acl_rules": "No sharing rules yet. Add a device above when access control is on.",
- "sharing_help": "Control who may read, change, or delete files in your shared folder.",
- "devices_help": "Connect to another person’s sync ID. Both sides should be syncing.",
- "remote_help": "Peek at a connected device’s files and pull one into your folder.",
- "browse_folder": "Browse",
- "open_folder": "Open",
- "browser_title": "Choose sync folder",
- "browser_hint": "Browse folders for this identity. Works the same on desktop, web, and mobile.",
- "browser_up": "Up",
- "browser_loading": "Loading folders...",
- "browser_empty": "No subfolders here. Create one below, or select this folder.",
- "browser_new": "New folder",
- "browser_new_placeholder": "Folder name",
- "browser_select": "Use this folder",
- "browser_created": "Folder created",
- "folder_selected": "Folder selected",
- "path_copied": "Folder path copied",
- "stop_before_change_folder": "Stop syncing before changing the folder.",
- "peer_connected": "Connected",
- "peer_disconnected": "Disconnected",
- "peer_unknown": "Unknown",
- "started": "Syncing started",
- "stopped": "Syncing stopped",
- "announced": "Announce sent",
- "connected": "Connecting to device...",
- "disconnected": "Device removed",
- "browse_done": "Remote file list ready",
- "download_started": "Download started",
- "file_updated": "A file changed",
- "file_deleted": "A file was removed",
- "copied": "Copied to clipboard",
- "error": "File Sync error"
- },
- "rnsh": {
- "remote_shell": "Remote shell",
- "usage_title": "Usage",
- "usage_hint": "Create connect or listen sessions, then manage them on the Sessions tab. Output streams live while MeshChatX is running.",
- "tab_sessions": "Sessions",
- "tab_sessions_short": "Sessions",
- "tab_connect": "Connect",
- "tab_connect_short": "Connect",
- "tab_listen": "Listen",
- "tab_listen_short": "Listen",
- "fullscreen": "Full screen",
- "exit_fullscreen": "Exit full screen",
- "show_sessions": "Sessions",
- "hide_sessions": "Back to terminal",
- "sessions": "Sessions",
- "refresh": "Refresh",
- "name": "Session name",
- "name_placeholder": "Ops shell",
- "listen_mode": "Listen",
- "destination_hash": "Destination hash",
- "destination_placeholder": "7a55144adf826958a9529a3bcf08b149",
- "allowed_hashes": "Allowed identity hashes",
- "allowed_hashes_placeholder": "One hash per line",
- "remote_command": "Remote command",
- "default_command": "Default command",
- "command_placeholder": "/bin/bash --login",
- "mirror_exit_code": "Mirror exit code (-m)",
- "no_id": "No local identity (-N)",
- "no_auth": "Allow any identity (-n)",
- "create_and_start": "Create and start",
- "session_output": "Session output",
- "no_command_yet": "No command started yet",
- "start": "Start",
- "stop": "Stop",
- "clear": "Clear",
- "remove": "Remove",
- "send_line": "Send",
- "command_input_placeholder": "Type command input...",
- "status_running": "Running",
- "status_stopped": "Stopped",
- "status_failed": "Failed",
- "no_sessions": "No sessions created yet.",
- "unnamed_session": "Unnamed session",
- "select_or_create_session": "Select or create a session to begin.",
- "no_output_yet": "No output yet.",
- "destination_required": "Destination hash is required for connect mode.",
- "session_created": "Session created",
- "session_started": "Session started",
- "session_stopped": "Session stopped",
- "session_removed": "Session removed",
- "output_cleared": "Output cleared",
- "title": "RNSH - Reticulum Remote Shell",
- "description": "Manage persistent rnsh sessions",
- "failed_to_load_sessions": "Failed to load RNSH sessions",
- "failed_to_create_session": "Failed to create RNSH session",
- "failed_to_start_session": "Failed to start RNSH session",
- "failed_to_stop_session": "Failed to stop RNSH session",
- "failed_to_remove_session": "Failed to remove RNSH session",
- "failed_to_clear_output": "Failed to clear RNSH output",
- "failed_to_send_input": "Failed to send RNSH input",
- "listening_on": "Listening on",
- "waiting_for_address": "Waiting for listener address...",
- "copy_address": "Copy",
- "address_copied": "Address copied to clipboard",
- "failed_to_copy_address": "Failed to copy address",
- "config_dir": "Reticulum config directory",
- "config_dir_placeholder": "Defaults to the app's Reticulum config",
- "config_dir_hint": "Leave blank to share the MeshChatX Reticulum instance. Override only to use a separate Reticulum config directory."
- },
- "rnx": {
- "remote_exec": "Remote execution",
- "usage_title": "Usage",
- "usage_hint": "Run one-shot remote commands or start an rnx listener. Manage active sessions on the Sessions tab. Output streams while MeshChatX is running.",
- "tab_sessions": "Sessions",
- "tab_sessions_short": "Sessions",
- "tab_execute": "Execute",
- "tab_execute_short": "Execute",
- "tab_listen": "Listen",
- "tab_listen_short": "Listen",
- "fullscreen": "Full screen",
- "exit_fullscreen": "Exit full screen",
- "show_sessions": "Sessions",
- "hide_sessions": "Back to output",
- "sessions": "Sessions",
- "refresh": "Refresh",
- "name": "Session name",
- "name_placeholder": "Remote uptime",
- "listen_mode": "Listen",
- "destination_hash": "Listener destination hash",
- "destination_placeholder": "7a55144adf826958a9529a3bcf08b149",
- "allowed_hashes": "Allowed identity hashes",
- "allowed_hashes_placeholder": "One hash per line",
- "remote_command": "Command",
- "command_placeholder": "uname -a",
- "mirror_exit_code": "Mirror exit code (-m)",
- "no_id": "No local identity (-N)",
- "no_auth": "Allow any identity (-n)",
- "detailed": "Detailed result (-d)",
- "interactive": "Interactive mode (-x)",
- "timeout": "Connect timeout (seconds)",
- "result_timeout": "Result download timeout (seconds)",
- "stdout_limit": "Stdout limit (bytes)",
- "stderr_limit": "Stderr limit (bytes)",
- "create_and_start": "Create and start",
- "session_output": "Session output",
- "no_command_yet": "No command started yet",
- "start": "Start",
- "stop": "Stop",
- "clear": "Clear",
- "remove": "Remove",
- "send_line": "Send",
- "command_input_placeholder": "Type interactive input...",
- "status_running": "Running",
- "status_stopped": "Stopped",
- "status_failed": "Failed",
- "no_sessions": "No sessions created yet.",
- "unnamed_session": "Unnamed session",
- "select_or_create_session": "Select or create a session to begin.",
- "no_output_yet": "No output yet.",
- "destination_required": "Destination hash is required for execute mode.",
- "command_required": "A command is required for execute mode.",
- "session_created": "Session created",
- "session_started": "Session started",
- "session_stopped": "Session stopped",
- "session_removed": "Session removed",
- "output_cleared": "Output cleared",
- "title": "RNX - Reticulum Remote Execution",
- "description": "Execute commands on remote systems over Reticulum",
- "failed_to_load_sessions": "Failed to load RNX sessions",
- "failed_to_create_session": "Failed to create RNX session",
- "failed_to_start_session": "Failed to start RNX session",
- "failed_to_stop_session": "Failed to stop RNX session",
- "failed_to_remove_session": "Failed to remove RNX session",
- "failed_to_clear_output": "Failed to clear RNX output",
- "failed_to_send_input": "Failed to send RNX input",
- "listening_on": "Listening on",
- "waiting_for_address": "Waiting for listener address...",
- "copy_address": "Copy",
- "address_copied": "Address copied to clipboard",
- "failed_to_copy_address": "Failed to copy address",
- "config_dir": "Reticulum config directory",
- "config_dir_placeholder": "Defaults to the app's Reticulum config",
- "config_dir_hint": "Leave blank to share the MeshChatX Reticulum instance. Override only to use a separate Reticulum config directory."
- },
- "rnprobe": {
- "network_diagnostics": "Network Diagnostics",
- "title": "RNProbe - Destination Probe",
- "description": "Send probes with configurable size to measure RTT and delivery.",
- "destination_hash": "Destination Hash",
- "full_destination_name": "Full Destination Name",
- "probe_size_bytes": "Probe Size (bytes)",
- "number_of_probes": "Number of Probes",
- "wait_between_probes": "Wait Between Probes (seconds)",
- "start_probe": "Start Probe",
- "stop": "Stop",
- "clear_results": "Clear Results",
- "summary": "Summary",
- "sent": "Sent",
- "delivered": "Delivered",
- "timeouts": "Timeouts",
- "failed": "Failed",
- "probe_results": "Probe Results",
- "probe_responses_realtime": "Probe responses in real time",
- "no_probes_yet": "No probes yet. Start a probe to test connectivity.",
- "probe_number": "Probe #{number}",
- "bytes": "bytes",
- "hops": "Hops",
- "rtt": "RTT",
- "rssi": "RSSI",
- "snr": "SNR",
- "quality": "Quality",
- "timeout": "Timeout",
- "invalid_hash": "Invalid destination hash!",
- "provide_full_name": "Please provide a full destination name!",
- "failed_to_probe": "Failed to probe destination"
- },
- "rnstatus": {
- "network_diagnostics": "Network Diagnostics",
- "title": "RNStatus - Network Status",
- "description": "Per-interface statistics and link status from your RNS instance.",
- "refresh": "Refresh",
- "include_link_stats": "Include Link Stats",
- "sort_by": "Sort by:",
- "none": "None",
- "bitrate": "Bitrate",
- "rx_bytes": "RX Bytes",
- "tx_bytes": "TX Bytes",
- "total_traffic": "Total Traffic",
- "announces": "Announces",
- "active_links": "Active Links: {count}",
- "no_interfaces_found": "No interfaces found. Click refresh to load status.",
- "mode": "Mode",
- "rx_packets": "RX Packets",
- "tx_packets": "TX Packets",
- "clients": "Clients",
- "peers_reachable": "reachable",
- "noise_floor": "Noise Floor",
- "interference": "Interference",
- "cpu_load": "CPU Load",
- "cpu_temp": "CPU Temp",
- "memory_load": "Memory Load",
- "battery": "Battery",
- "network": "Network",
- "incoming_announces": "Incoming Announces",
- "outgoing_announces": "Outgoing Announces",
- "airtime": "Airtime",
- "channel_load": "Channel Load",
- "blackhole_label": "Blackhole: {state}",
- "blackhole_publishing": "Publishing",
- "blackhole_inactive": "Inactive",
- "failed_refresh": "Failed to refresh RNStatus",
- "reloading": "Reloading RNS...",
- "remote_query": "Remote query",
- "remote_query_hint": "Query a remote transport instance that has remote management enabled and your management identity on its allow-list.",
- "remote_transport_hash": "Remote transport identity hash",
- "remote_transport_placeholder": "32-character hex hash",
- "remote_timeout": "Timeout (seconds)",
- "remote_active": "Showing remote status for {hash}",
- "use_local": "Use local instance"
- },
- "remote_mgmt": {
- "management_identity": "Management identity",
- "select_identity": "Select identity file",
- "create_identity": "Create",
- "create_identity_prompt": "Name for the new management identity file",
- "identity_created": "Management identity created",
- "failed_load_identities": "Failed to load management identities",
- "failed_create_identity": "Failed to create management identity"
- },
- "translator": {
- "text_translation": "Text Translation",
- "title": "Translator",
- "description": "Translate text via a LibreTranslate server or local Argos Translate.",
- "argos_translate": "Argos Translate",
- "libretranslate": "LibreTranslate",
- "api_server": "LibreTranslate API Server",
- "api_server_description": "Enter the base URL of your LibreTranslate server (e.g., https://libretranslate.com or http://localhost:5000).",
- "api_key_optional": "LibreTranslate API key (optional)",
- "api_key_placeholder": "Leave empty unless your provider requires one",
- "api_key_description": "If required, LibreTranslate expects this as api_key in the JSON translate body and optionally on the languages request. Omit for typical self-hosted instances.",
- "source_language": "Source Language",
- "auto_detect": "Auto-detect",
- "target_language": "Target Language",
- "select_target_language": "Select target language",
- "argos_not_detected": "Argos Translate not detected",
- "argos_not_detected_desc": "To use local translation, you must install the Argos Translate package using one of the following methods:",
- "method_pip_venv": "Method 1: pip (venv)",
- "method_pipx": "Method 2: pipx",
- "note_restart_required": "Note: After installation, you may need to restart the application and install language packages via the Argos Translate CLI.",
- "no_language_packages": "No language packages detected",
- "no_language_packages_desc": "Argos Translate is installed but no language packages are available. Install language packages using one of the following commands:",
- "install_all_languages": "Install all languages",
- "install_specific_pair": "Install specific language pair (example: English to German)",
- "after_install_note": "After installing language packages, click \"Refresh Languages\" to reload available languages.",
- "text_to_translate": "Text to Translate",
- "enter_text_placeholder": "Enter text to translate...",
- "translate": "Translate",
- "swap": "Swap",
- "clear": "Clear",
- "translation": "Translation",
- "source": "Source",
- "detected": "Detected",
- "available_languages": "Available Languages",
- "languages_loaded_from": "Languages are loaded from LibreTranslate API or Argos Translate packages.",
- "refresh_languages": "Refresh Languages",
- "failed_load_languages": "Failed to load languages. Make sure LibreTranslate is running or Argos Translate is installed.",
- "failed_translate": "Translation failed. Make sure LibreTranslate is running or Argos Translate is installed.",
- "select_languages_warning": "Please select both source and target languages.",
- "auto_detect_not_supported": "Auto-detection is not supported with Argos Translate. Please select a source language.",
- "copied_to_clipboard": "Copied to clipboard"
- },
- "call": {
- "phone": "Phone",
- "voicemail": "Voicemail",
- "settings": "Settings",
- "unknown": "Unknown",
- "call_ended": "Call Ended",
- "call_declined": "Call Declined",
- "initiation": "Initiating...",
- "send_to_voicemail": "Send to Voicemail",
- "incoming_call": "Incoming Call...",
- "busy": "Busy...",
- "rejected": "Rejected...",
- "calling": "Calling...",
- "available": "Available...",
- "ringing": "Ringing...",
- "connecting": "Connecting...",
- "establishing_link": "Establishing link...",
- "connected": "Connected",
- "accept": "Accept",
- "decline": "Decline",
- "hangup": "Hangup",
- "answer_call": "Answer Call",
- "decline_call": "Decline Call",
- "hangup_call": "Hangup Call",
- "mute_mic": "Mute Mic",
- "unmute_mic": "Unmute Mic",
- "mute_speaker": "Mute Speaker",
- "unmute_speaker": "Unmute Speaker",
- "telephone": "Telephone",
- "enter_identity_hash_to_call": "Enter an identity to call.",
- "identity_hash": "Identity Hash",
- "call_history": "Call History",
- "call_back": "Call Back",
- "no_voicemails": "No Voicemails",
- "voicemails_will_appear_here": "When people leave you messages, they'll show up here.",
- "voicemail_inbox": "Voicemail Inbox",
- "messages": "Messages",
- "delete": "Delete",
- "voicemail_settings": "Voicemail Settings",
- "dependencies_missing": "Dependencies Missing",
- "voicemail_requires_espeak": "Voicemail requires `espeak-ng` to generate greetings. Please install it on your system.",
- "enable_voicemail": "Enable Voicemail",
- "enable_voicemail_description": "Accept calls automatically and record messages",
- "greeting_message": "Greeting Message",
- "enter_greeting_text": "Enter greeting text...",
- "greeting_text_description": "This text will be converted to speech using eSpeak NG.",
- "tts_speed": "TTS Speed",
- "tts_pitch": "TTS Pitch",
- "tts_voice": "TTS Voice",
- "tts_word_gap": "TTS Word Gap",
- "save_and_generate": "Save & Generate",
- "generating": "Generating...",
- "preview_greeting": "Preview Greeting",
- "stop_preview": "Stop Preview",
- "answer_delay_seconds": "Answer Delay (s)",
- "max_recording_seconds": "Max Recording (s)",
- "recording_voicemail": "Recording Voicemail",
- "call_status": "Call Status",
- "call_recording": "Call Recorder",
- "call_recording_description": "Record voice calls to storage",
- "recordings": "Recordings",
- "no_recordings": "No recordings found",
- "remote_rx": "Remote (RX)",
- "local_tx": "Local (TX)",
- "download_rx": "Download RX",
- "download_tx": "Download TX",
- "minimize": "Minimize",
- "active": "Active",
- "default_quality": "Default Quality",
- "default_duplex": "Default Duplex",
- "full_duplex": "Full Duplex",
- "half_duplex": "Half Duplex",
- "switch_to_full_duplex": "Switch to full duplex",
- "switch_to_half_duplex": "Switch to half duplex",
- "ptt_hold_to_talk": "Hold to Talk",
- "ptt_transmitting": "Transmitting",
- "ptt_listening": "Listening",
- "ptt_hold_hint": "Hold to unsquelch transmit on this half-duplex call",
- "ptt_spacebar_hint": "Hold Space to talk",
- "tx_packets": "TX Packets",
- "rx_packets": "RX Packets",
- "tx_data": "TX Data",
- "rx_data": "RX Data",
- "tx_rate": "TX Rate",
- "rx_rate": "RX Rate",
- "failed_to_switch_call_mode": "Failed to switch duplex mode",
- "failed_to_set_ptt": "Failed to set push-to-talk",
- "active_call": "Active Call",
- "status": "Status",
- "failed_to_answer_call": "Failed to answer call",
- "failed_to_hangup_call": "Failed to hangup call",
- "failed_to_toggle_microphone": "Failed to toggle microphone",
- "failed_to_toggle_speaker": "Failed to toggle speaker",
- "failed_to_switch_audio_profile": "Failed to switch audio profile",
- "codec2_unavailable": "Codec2 is not available on this device. Low-bandwidth call profiles are hidden.",
- "codec2_profile_remapped": "Codec2 is unavailable so the call profile was switched to Opus.",
- "failed_to_initiate_call": "Failed to initiate call",
- "enter_identity_hash_to_call_error": "Enter an identity to call",
- "failed_to_save_settings": "Failed to save settings",
- "do_not_disturb": "Do Not Disturb",
- "load_more": "Load More",
- "go_to_phone_page": "Go to Phone Page",
- "search_history": "Search history...",
- "allow_calls_from_contacts_only": "Only allow calls from contacts",
- "settings_saved": "Settings saved",
- "greeting_generated_successfully": "Greeting generated successfully",
- "failed_to_generate_greeting": "Failed to generate greeting",
- "no_greeting_audio_found": "No greeting audio found. Please generate one first.",
- "voicemail_deleted": "Voicemail deleted",
- "failed_to_delete_voicemail": "Failed to delete voicemail",
- "ringtone": "Ringtone",
- "ringtone_settings": "Ringtone Settings",
- "enable_custom_ringtone": "Enable Custom Ringtone",
- "enable_custom_ringtone_description": "Play a custom audio file for all incoming calls",
- "upload_ringtone": "Upload Ringtone",
- "uploading_ringtone": "Uploading Ringtone...",
- "no_custom_ringtone_uploaded": "No custom ringtone uploaded",
- "supports_audio_formats": "Supports MP3, OGG, WAV, M4A, FLAC. Will be converted to Opus.",
- "preview_ringtone": "Preview Ringtone",
- "remove_ringtone": "Remove Ringtone",
- "ringtone_uploaded_successfully": "Ringtone uploaded successfully",
- "failed_to_upload_ringtone": "Failed to upload ringtone",
- "ringtone_deleted": "Ringtone deleted",
- "failed_to_delete_ringtone": "Failed to delete ringtone",
- "no_ringtone_audio_found": "No ringtone audio found. Please upload one first.",
- "ringtone_volume": "Ringtone Volume",
- "default_ringtone": "Default Ringtone",
- "ringtone_for_non_contacts": "Ringtone for callers not in your contacts",
- "primary_system_default": "Primary (System Default)",
- "random": "Random",
- "edit_contact": "Edit Contact",
- "add_contact": "Add Contact",
- "contact_name": "Contact Name",
- "preferred_ringtone": "Preferred Ringtone",
- "default_global_setting": "Default (Global Setting)",
- "uploaded_ringtones": "Uploaded Ringtones",
- "save_contact": "Save Contact",
- "primary_ringtone_set": "Primary ringtone set",
- "failed_to_set_primary_ringtone": "Failed to set primary ringtone",
- "failed_to_update_ringtone_name": "Failed to update ringtone name",
- "failed_to_play_ringtone": "Failed to play ringtone",
- "failed_to_play_voicemail": "Failed to play voicemail",
- "web_audio_not_available": "Web audio not available",
- "no_audio_input_found": "No input audio device found. Check microphone and permissions.",
- "microphone_permission_denied": "Microphone permission denied. Allow access and try again.",
- "failed_to_update_dnd": "Failed to update Do Not Disturb status",
- "failed_to_update_call_settings": "Failed to update call settings",
- "failed_to_update_recording_status": "Failed to update call recording status",
- "call_history_cleared": "Call history cleared",
- "failed_to_clear_call_history": "Failed to clear call history",
- "identity_banished": "Identity banished",
- "failed_to_banish_identity": "Failed to banish identity",
- "name_and_hash_required": "Name and identity hash required",
- "contact_updated": "Contact updated",
- "contact_added": "Contact added",
- "contact_deleted": "Contact deleted",
- "failed_to_delete_contact": "Failed to delete contact",
- "hash_copied": "Hash copied to clipboard",
- "failed_to_copy_hash": "Failed to copy hash",
- "greeting_uploaded_successfully": "Greeting uploaded successfully",
- "greeting_deleted": "Greeting deleted",
- "failed_to_delete_greeting": "Failed to delete greeting",
- "failed_to_start_recording_greeting": "Failed to start recording greeting",
- "greeting_recorded_from_mic": "Greeting recorded from mic",
- "failed_to_stop_recording_greeting": "Failed to stop recording greeting",
- "failed_to_load_recording": "Failed to load recording audio",
- "recording_deleted": "Recording deleted",
- "failed_to_delete_recording": "Failed to delete recording",
- "call_sent_to_voicemail": "Call sent to voicemail",
- "failed_to_send_to_voicemail": "Failed to send call to voicemail",
- "failed_load_audio_edit": "Failed to load audio for editing",
- "ringtone_saved": "Ringtone saved successfully",
- "failed_save_ringtone": "Failed to save edited ringtone"
- },
- "contacts": {
- "title": "Contacts",
- "description": "Manage your contact list and share/import identities.",
- "search_placeholder": "Search contacts...",
- "actions": "Actions",
- "no_contacts": "No contacts found",
- "load_more": "Load more",
- "add_contact": "Add Contact",
- "contact_name_optional": "Contact Name (Optional)",
- "contact_name_placeholder": "e.g. Field Node",
- "hash_or_uri": "LXMF Hash or URI",
- "hash_or_uri_placeholder": "32-char hash, lxmf://..., or lxma://...",
- "paste": "Paste",
- "failed_load_contacts": "Failed to load contacts",
- "invalid_contact_input": "Invalid input. Use a 32-char hash or lxma:// URI.",
- "contact_already_exists": "Contact already exists",
- "contact_added": "Contact added",
- "failed_add_contact": "Failed to add contact",
- "remove_contact": "Remove Contact",
- "remove_contact_confirm": "Remove this contact?",
- "contact_removed": "Contact removed",
- "failed_remove_contact": "Failed to remove contact",
- "share_contact": "Share Contact URI",
- "copy_contact_uri": "Copy Contact URI",
- "contact_uri_copied": "Contact URI copied to clipboard",
- "failed_build_contact_uri": "Could not build a share URI for this contact",
- "share_my_identity": "Share My Identity",
- "identity_uri_copied": "Identity URI copied to clipboard",
- "share": "Share",
- "scan_qr": "Scan QR Code",
- "scanner_hint": "Point camera at an identity QR code.",
- "camera_not_supported": "Camera QR scanning is not supported in this browser.",
- "camera_permission_denied": "Camera permission denied. Please allow camera access.",
- "camera_not_found": "No camera device found.",
- "camera_failed": "Failed to start camera scanner.",
- "qr_scanned": "QR code scanned",
- "importing_lxma": "Importing LXMA contact...",
- "send_message": "Send Message",
- "call_contact": "Call",
- "export_contacts": "Export",
- "import_contacts": "Import",
- "export_success": "Contacts exported",
- "export_failed": "Failed to export contacts",
- "import_success": "Imported {added} contact(s)",
- "import_failed": "Failed to import contacts",
- "import_modal_title": "Import Contacts",
- "import_file_hint": "Select a JSON file exported from Contacts.",
- "edit_contact": "Edit Contact",
- "enter_contact_name": "Enter contact name",
- "contact_updated": "Contact updated",
- "failed_update_contact": "Failed to update contact"
- },
- "android_storage": {
- "setup_title": "Choose where MeshChatX stores data",
- "setup_desc": "File-manager storage is recommended. You can browse and back up your identity and messages under Android/data/com.meshchatx/files/. The app restarts when you change this during setup.",
- "setup_external_title": "File-manager storage (recommended)",
- "setup_external_desc": "Android/data/com.meshchatx/files/meshchatx, visible in many file managers.",
- "setup_internal_title": "Private app storage",
- "setup_internal_desc": "Hidden from file managers. Only this app can access it.",
- "setup_continue": "Continue",
- "upgrade_title": "Move data to file-manager storage?",
- "upgrade_desc": "Your MeshChatX data is in private app storage. Copy it to the file-manager folder so you can browse and back up files, then restart the app.",
- "upgrade_copy": "Copy and restart",
- "upgrade_stay_internal": "Stay on private storage",
- "working": "Preparing…",
- "copy_restart_hint": "Copy scheduled. Closing the app. Open MeshChatX again to finish.",
- "restart_to_apply": "Restart the app to apply your storage choice.",
- "failed": "Could not update storage location."
- },
- "tutorial": {
- "title": "Getting Started",
- "welcome": "Welcome to",
- "welcome_desc": "The future of off-grid communication. Secure, decentralized, and unstoppable.",
- "migration_title": "Earlier install folder detected",
- "migration_desc": "Data was found in .reticulum-meshchat. Copy it into .reticulum-meshchatx and restart, or start fresh and leave the old folder as-is.",
- "migration_migrate": "Copy to .reticulum-meshchatx",
- "migration_fresh": "Start fresh",
- "migration_working": "Updating storage…",
- "migration_done_restart": "Restart the app to use the new folder.",
- "migration_failed": "Could not update storage.",
- "security": "Security & Performance",
- "security_desc": "Massive improvements in speed, security, and crash recovery.",
- "security_desc_page": "Massive improvements in speed, security, and integrity with built-in crash recovery.",
- "maps": "Maps",
- "maps_desc": "OpenLayers w/ MBTiles support (export and offline support) and custom API endpoints.",
- "maps_desc_page": "OpenLayers support with offline MBTiles (export and offline support) and custom API endpoints for online maps.",
- "voice": "Full LXST Voice",
- "voice_desc": "Voicemail, ringtones, phonebook, and contact sharing.",
- "voice_desc_page": "Crystal clear voice calls over mesh. Voicemail, custom ringtones, and phonebook discovery.",
- "tools": "Built-in extras",
- "tools_desc": "Micron Editor, NomadNet, RNS tools, Docs.",
- "tools_desc_page": "Micron editor, NomadNet nodes, RNS tools, and integrated documentation.",
- "archiver": "Archiver",
- "archiver_desc": "Manual and Automated page archiving.",
- "archiver_desc_page": "Manual and Automated page archiving tools for offline browsing.",
- "banishment": "Banishment",
- "banishment_desc": "Banish annoying people, nodes and support for RNS blackhole level dropping their announces",
- "palette": "Command Palette + Keybindings",
- "palette_desc": "Navigate everything instantly and customize shortcuts.",
- "palette_desc_page": "Navigate the entire application and customize your workflow with instant shortcuts.",
- "i18n": "i18n Support",
- "i18n_desc": "Available in English, German, Italian, Russian, Dutch, French, and Spanish.",
- "i18n_desc_page": "Full internationalization support for English, German, Italian, Russian, Dutch, French, and Spanish.",
- "more_features": "+ many more features!",
- "connect": "Connect to the Mesh",
- "connect_desc": "Choose how you want to connect. Discovery and bootstrap nodes can be configured later on the Interfaces page.",
- "connect_desc_page": "Use local LAN discovery or configure custom interfaces from the Interfaces page. Auto-discovery and bootstrap nodes are available there for advanced setup.",
- "identity_title": "Identity",
- "identity_desc": "Choose a new identity or import an existing key before connecting.",
- "identity_desc_page": "Choose a new identity or import an existing key before connecting to the mesh.",
- "identity_new": "New Identity (default)",
- "identity_new_desc": "Keep the default identity and optionally set your display name.",
- "identity_import": "Import existing identity",
- "identity_import_desc": "Import from an identity file or paste a base32 key.",
- "identity_set_name": "Set name",
- "identity_upload_file": "Import identity file",
- "identity_base32_placeholder": "Paste base32 private identity key",
- "identity_import_required": "Choose an identity file or paste a base32 key to import.",
- "identity_import_failed": "Failed to import identity",
- "identity_import_empty_file": "Identity file is empty.",
- "identity_import_file_too_large": "Identity file is too large.",
- "identity_import_key_only_hint": "This imports your identity key only. To restore messages and settings, use About → Restore from File with a database backup zip.",
- "identity_file_overrides_base32": "A selected file will be used instead of the pasted base32 key.",
- "identity_import_pending_activate": "You imported an identity but have not activated it yet. Activate it now?",
- "identity_import_pending_kept": "Imported identity was saved but not activated. Switch to it later from Identities.",
- "identity_default_delete_failed": "Imported identity is active, but the default identity could not be removed.",
- "identity_name_update_failed": "Failed to set display name",
- "identity_switch_failed": "Failed to activate imported identity",
- "suggested_networks": "Suggested Public Networks",
- "suggested_relays": "Suggested Relays",
- "use": "Use",
- "online": "Online",
- "custom_interfaces": "Custom Interfaces",
- "custom_interfaces_desc": "Want to add a bespoke interface? Head to the Interfaces page to configure a custom connection.",
- "custom_interfaces_desc_page": "For any private relays or hardware you manage yourself, add them through the dedicated Interfaces page.",
- "open_interfaces": "Open Interfaces page",
- "learn_create": "Learn & Create",
- "learn_create_desc": "Discover how to use MeshChatX to its full potential.",
- "learn_create_desc_page": "Discover how to use MeshChatX to its full potential and create your own content.",
- "learn_create_more": "More to explore",
- "documentation": "Documentation",
- "documentation_desc": "Read the official MeshChatX and Reticulum documentation.",
- "documentation_desc_page": "Guides for MeshChatX and the Reticulum stack.",
- "meshchatx_docs": "MeshChatX Docs",
- "reticulum_docs": "Reticulum Docs",
- "read_meshchatx_docs": "Read MeshChatX Docs",
- "reticulum_manual": "Reticulum Network Manual",
- "micron_editor": "Micron Editor",
- "micron_editor_desc": "Take a look at the Micron Editor for a guide on creating mesh-native pages.",
- "micron_editor_desc_page": "Learn how to create mesh-native pages and interactive content using the Micron markup language.",
- "open_micron_editor": "Open Micron Editor",
- "open_mesh_server": "Open Mesh Server",
- "open_identities": "Identities",
- "identities_card_title": "Multiple identities",
- "identities_card_desc": "Switch personas, export keys, and keep separate profiles on one device.",
- "identities_card_desc_page": "Run multiple Reticulum identities on one install: create and delete personas, switch who you are on the mesh, and export keys when you need them.",
- "paper_messages": "Explore NomadNet Nodes",
- "paper_messages_desc": "Browse Nomad pages and services.",
- "send_messages": "Send Messages",
- "send_messages_desc": "Start a secure conversation.",
- "explore_nodes": "Visualize Nodes",
- "explore_nodes_desc": "Open the map or network graph.",
- "voice_calls": "Voice Calls",
- "voice_calls_desc": "Place and receive voice calls.",
- "ready": "Ready to Roll!",
- "ready_desc": "Everything is set up. Reticulum has been restarted with your new configuration.",
- "ready_desc_page": "MeshChatX is now configured and Reticulum has been hot-reloaded with your new settings.",
- "privacy_title": "Privacy & security",
- "privacy_desc": "Optional hardening for this device. You can change these any time in Settings → Privacy.",
- "privacy_later_hint": "Recommended for sensitive use. Leaving options off is fine for casual installs.",
- "docker_note": "Reticulum was restarted automatically. If anything looks off, you can restart MeshChatX from the menu.",
- "restart_required": "Reticulum Restarted",
- "restart_desc_page": "Reticulum was hot-reloaded with your new configuration. No manual restart is required for normal use.",
- "back": "Back",
- "skip": "Skip",
- "next": "Next",
- "restart_start": "Start MeshChatX",
- "finish_setup": "Finish Setup",
- "skip_setup": "Skip Setup",
- "continue": "Continue",
- "skip_confirm": "Are you sure you want to skip the setup? You'll need to manually add interfaces later.",
- "discovery_question": "Do you want to use community interface discovering and auto-connect?",
- "discovery_desc": "This allows MeshChatX to automatically find and connect to public community nodes near you or on the internet.",
- "yes": "Yes, use discovery",
- "no": "No, manual setup",
- "discovery_enabled": "Community discovery enabled",
- "failed_enable_discovery": "Failed to enable discovery",
- "mode_discovery_title": "Auto-Discovery",
- "mode_discovery_desc": "Find and auto-connect to nearby and internet-reachable Reticulum nodes. You'll pick one or more bootstrap nodes next.",
- "mode_local_title": "Local Network Only",
- "mode_local_desc": "Use Reticulum on your LAN/Wi-Fi only. Adds an AutoInterface for zero-config local mesh.",
- "mode_manual_title": "Manual Setup",
- "mode_manual_desc": "Skip for now and configure interfaces, discovery, and bootstrap nodes from the Interfaces page later.",
- "mode_change_later": "You can change connection settings any time from Reticulum settings.",
- "bootstrap_title": "Pick Bootstrap Nodes",
- "bootstrap_desc": "Bootstrap nodes give discovery a starting point. Select one or more, then continue.",
- "bootstrap_pick_random_tcp": "Pick 4 random TCP nodes",
- "bootstrap_pick_random_none": "No suitable TCP community presets are available. Refresh community presets or pick nodes manually (discovered lists are not used for random pick).",
- "bootstrap_pick_random_done": "Selected {count}: {names}",
- "bootstrap_selected_nodes_heading": "Selected nodes",
- "bootstrap_search_placeholder": "Search discovered and community…",
- "bootstrap_search_no_match": "No nodes match your search",
- "bootstrap_search_clear": "Clear search",
- "bootstrap_only_label": "Bootstrap-only for added interfaces",
- "bootstrap_only_hint": "When on, every interface you add in this step is created as bootstrap-only, which Reticulum can drop after enough links auto-connect. The default is adjustable in Interfaces, Discovery settings.",
- "bootstrap_desc_page": "Bootstrap nodes give Reticulum a starting point so it can discover the rest of the network. Select one or more from discovered or community nodes below.",
- "bootstrap_discovered": "Discovered Nodes",
- "bootstrap_community": "Community Nodes",
- "bootstrap_selected": "{count} selected",
- "bootstrap_skip": "Skip for now",
- "bootstrap_confirm": "Add selected and restart RNS",
- "bootstrap_added": "Added {count} bootstrap interface(s)",
- "bootstrap_pick_at_least_one": "Pick at least one bootstrap node",
- "failed_add_bootstrap": "Failed to add bootstrap interface",
- "failed_add_bootstrap_none": "No bootstrap interfaces were added. Check your selection and try again.",
- "failed_save_bootstrap_only": "Failed to save bootstrap-only preference",
- "failed_enable_propagation": "Failed to enable auto-propagation",
- "connect_mode_required": "Choose a connection mode to continue",
- "local_added": "Local AutoInterface added",
- "failed_add_local": "Failed to add local interface",
- "failed_reload_rns": "Failed to restart Reticulum",
- "ready_finished": "Reticulum has been restarted with your new configuration",
- "propagation": "Propagation Node",
- "propagation_question": "Do you want to auto-configure propagation mode?",
- "propagation_desc": "Propagation nodes act like an always-on mailbox for your messages. They securely store messages while you are offline and deliver them as soon as you reconnect to the network.",
- "propagation_auto": "Auto-configure will automatically find and connect to the most reliable propagation node available. If no node is currently available, MeshChatX will wait for one to appear. Until then, offline message syncing will be unavailable.",
- "propagation_manual": "Or manually configure?",
- "propagation_manual_desc": "You can manually configure a propagation node later in settings, or leave it empty if you don't want to use one.",
- "propagation_enable_auto": "Enable Auto-Configuration",
- "propagation_skip_auto": "Configure Manually / Skip",
- "auto_propagation_enabled": "Auto-propagation enabled"
- },
- "command_palette": {
- "search_placeholder": "Search commands, routes, or peers",
- "no_results": "No results found for \"{query}\"",
- "group_actions": "Actions",
- "group_recent": "Recent Peers",
- "group_contacts": "Contacts",
- "footer_navigate": "Navigate",
- "footer_select": "Select",
- "nav_messages": "Messages",
- "nav_messages_desc": "Conversations and LXMF inbox",
- "nav_nomad": "Nomad Network",
- "nav_nomad_desc": "Browse Nomad pages and services",
- "nav_map": "Map",
- "nav_map_desc": "Geographic map and layers",
- "nav_paper": "Paper Messages",
- "nav_paper_desc": "Signed paper messages (QR)",
- "nav_call": "Voice Calls",
- "nav_call_desc": "Voice calls over the mesh",
- "nav_settings": "Settings",
- "nav_settings_desc": "App and transport preferences",
- "nav_ping": "Ping",
- "nav_ping_desc": "Measure RTT to a destination",
- "nav_rnprobe": "RNProbe",
- "nav_rnprobe_desc": "Probe reachability and delay",
- "nav_rncp": "RNCP",
- "nav_rncp_desc": "File copy over Reticulum (RNCP)",
- "nav_rns_filesync": "File Sync",
- "nav_rns_filesync_desc": "Shared folders over the mesh",
- "nav_rnsh": "RNSH",
- "nav_rnsh_desc": "Remote shell over Reticulum",
- "nav_rnx": "RNX",
- "nav_rnx_desc": "Remote command execution over Reticulum",
- "nav_rnstatus": "RNStatus",
- "nav_rnstatus_desc": "Interface and link statistics",
- "nav_rnpath": "RNPath",
- "nav_rnpath_desc": "Paths and announce queues",
- "nav_rnpath_trace": "RNPath Trace",
- "nav_rnpath_trace_desc": "Hop list toward a hash",
- "nav_translator": "Translator",
- "nav_translator_desc": "Translate message text",
- "nav_forwarder": "Forwarder",
- "nav_forwarder_desc": "LXMF forwarding rules",
- "nav_documentation": "Documentation",
- "nav_documentation_desc": "Offline manuals",
- "nav_repository_server": "Repository server",
- "nav_repository_server_desc": "MeshChatX wheels, Reticulum packages, and your files over HTTP",
- "nav_micron_editor": "Micron Editor",
- "nav_micron_editor_desc": "Edit Micron markup",
- "nav_reticulum_config_editor": "Reticulum Config Editor",
- "nav_reticulum_config_editor_desc": "Edit the raw Reticulum config file",
- "nav_rnode_flasher": "RNode Flasher",
- "nav_rnode_flasher_desc": "Device firmware update",
- "nav_debug_logs": "Debug Logs",
- "nav_debug_logs_desc": "Application and access logs",
- "action_sync": "Sync Messages",
- "action_sync_desc": "Sync with your propagation node",
- "action_compose": "Compose Message",
- "action_compose_desc": "New LXMF message",
- "action_getting_started": "Getting Started",
- "action_getting_started_desc": "Open the setup guide",
- "action_changelog": "Changelog",
- "action_changelog_desc": "Recent changes"
- },
- "post_install": {
- "windows_screen_security_title": "Enable Windows screen security?",
- "windows_screen_security_desc": "MeshChatX can use Windows content protection (the same DRM-style display flag media apps use) so screenshots, recorders, and Windows Recall cannot capture the app window. You can change this later under Settings → Privacy.",
- "windows_screen_security_enable": "Enable screen security",
- "windows_screen_security_later": "Not now"
- }
+ "fetch_file": "Fetch File",
+ "listen": "Listen",
+ "destination_hash": "Destination Hash",
+ "file_path": "File Path",
+ "timeout_seconds": "Timeout (seconds)",
+ "disable_compression": "Disable compression",
+ "cancel": "Cancel",
+ "progress": "Progress",
+ "invalid_hash": "Invalid destination hash!",
+ "provide_file_path": "Please provide a file path!",
+ "file_sent_successfully": "File sent successfully. Transfer ID: {id}",
+ "failed_to_send": "Failed to send file",
+ "remote_file_path": "Remote File Path",
+ "save_path_optional": "Save Path (optional)",
+ "save_path_placeholder": "Leave empty for current directory",
+ "allow_overwrite": "Allow overwrite",
+ "provide_remote_file_path": "Please provide a remote file path!",
+ "file_fetched_successfully": "File fetched successfully. Saved to: {path}",
+ "failed_to_fetch": "Failed to fetch file",
+ "allowed_hashes": "Allowed Identity Hashes (one per line)",
+ "fetch_jail_path": "Fetch Jail Path (optional)",
+ "allow_fetch": "Allow fetch",
+ "start_listening": "Start Listening",
+ "stop_listening": "Stop Listening",
+ "listening_on": "Listening on:",
+ "provide_allowed_hash": "Please provide at least one allowed identity hash!",
+ "failed_to_start_listener": "Failed to start listener",
+ "failed_to_stop_listener": "Failed to stop listener",
+ "browse_file": "Browse",
+ "browse_folder": "Choose folder",
+ "receive_folder": "Received files folder",
+ "open_folder": "Open folder",
+ "show_in_folder": "Show in folder",
+ "received_file": "File received",
+ "receive_failed": "Receive failed",
+ "send_complete": "Send complete",
+ "fetch_complete": "Fetch complete",
+ "transfer_status": "Status",
+ "listening_active_background": "Listening continues in the background. Return here to manage or stop.",
+ "web_path_hint": "Browsers cannot expose full file paths. Use the desktop app to browse, or paste the full path to the file on this machine.",
+ "web_save_path_prompt": "Enter the absolute folder path on the machine running MeshChat (server) where the fetched file should be saved:"
+ },
+ "rns_filesync": {
+ "eyebrow": "Shared folders",
+ "title": "File Sync",
+ "description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
+ "usage_steps": "Quick start",
+ "step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
+ "step_2": "2. On Devices, paste their sync ID to connect. Manage local files under Files, or pull from Remote.",
+ "step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
+ "tab_folder": "Folder",
+ "tab_devices": "Devices",
+ "tab_files": "Files",
+ "tab_remote": "Remote",
+ "tab_sharing": "Sharing",
+ "tab_status": "Folder",
+ "tab_peers": "Devices",
+ "tab_browse": "Remote",
+ "tab_acl": "Sharing",
+ "sync_directory": "Folder to sync",
+ "sync_directory_placeholder": "Choose a folder under this identity",
+ "sync_directory_help": "Folders stay inside this identity's MeshChat storage. Use Browse on any device to pick or create one.",
+ "announce_interval": "Announce every (seconds)",
+ "announce_interval_help": "How often others are reminded you are available to sync.",
+ "monitor": "Watch folder for changes",
+ "start": "Start syncing",
+ "stop": "Stop syncing",
+ "announce": "Announce now",
+ "refresh": "Refresh",
+ "running": "Running",
+ "status_syncing": "Syncing",
+ "status_stopped": "Stopped",
+ "yes": "Yes",
+ "no": "No",
+ "peers_count": "Devices",
+ "files_count": "Files",
+ "destination_hash": "Sync ID",
+ "share_id": "Your sync ID",
+ "share_id_help": "Send this to friends so they can connect. Tap to copy.",
+ "last_progress": "Latest transfer",
+ "peer_hash_placeholder": "Friend's sync ID",
+ "connect": "Connect",
+ "disconnect": "Remove",
+ "no_peers": "No devices connected yet. Start syncing, then paste a friend’s sync ID.",
+ "no_files": "This folder is empty so far. Drop files in, or pull some from Remote.",
+ "manager_help": "Browse and manage files in your sync folder. Uploads and deletes stay inside this folder only.",
+ "manager_root": "(sync folder root)",
+ "manager_loading": "Loading files...",
+ "manager_empty": "This folder is empty. Upload a file or create a subfolder.",
+ "upload": "Upload",
+ "upload_done": "File uploaded",
+ "download_local": "Download",
+ "download_local_done": "Download started",
+ "delete": "Delete",
+ "delete_confirm": "Delete \"{name}\" from the sync folder?",
+ "delete_done": "Deleted",
+ "select_peer": "Choose a device",
+ "browse": "List files",
+ "no_remote_files": "No remote files yet. Connect a device, then list their files.",
+ "download": "Get file",
+ "acl_enforce": "Only allow listed devices (deny everyone else)",
+ "acl_grant": "Allow device",
+ "acl_updated": "Sharing rules updated",
+ "perm_read": "Read",
+ "perm_write": "Write",
+ "perm_delete": "Delete",
+ "no_acl_rules": "No sharing rules yet. Add a device above when access control is on.",
+ "sharing_help": "Control who may read, change, or delete files in your shared folder.",
+ "devices_help": "Connect to another person’s sync ID. Both sides should be syncing.",
+ "remote_help": "Peek at a connected device’s files and pull one into your folder.",
+ "browse_folder": "Browse",
+ "open_folder": "Open",
+ "browser_title": "Choose sync folder",
+ "browser_hint": "Browse folders for this identity. Works the same on desktop, web, and mobile.",
+ "browser_up": "Up",
+ "browser_loading": "Loading folders...",
+ "browser_empty": "No subfolders here. Create one below, or select this folder.",
+ "browser_new": "New folder",
+ "browser_new_placeholder": "Folder name",
+ "browser_select": "Use this folder",
+ "browser_created": "Folder created",
+ "folder_selected": "Folder selected",
+ "path_copied": "Folder path copied",
+ "stop_before_change_folder": "Stop syncing before changing the folder.",
+ "peer_connected": "Connected",
+ "peer_disconnected": "Disconnected",
+ "peer_unknown": "Unknown",
+ "started": "Syncing started",
+ "stopped": "Syncing stopped",
+ "announced": "Announce sent",
+ "connected": "Connecting to device...",
+ "disconnected": "Device removed",
+ "browse_done": "Remote file list ready",
+ "download_started": "Download started",
+ "file_updated": "A file changed",
+ "file_deleted": "A file was removed",
+ "copied": "Copied to clipboard",
+ "error": "File Sync error"
+ },
+ "rnsh": {
+ "remote_shell": "Remote shell",
+ "usage_title": "Usage",
+ "usage_hint": "Create connect or listen sessions, then manage them on the Sessions tab. Output streams live while MeshChatX is running.",
+ "tab_sessions": "Sessions",
+ "tab_sessions_short": "Sessions",
+ "tab_connect": "Connect",
+ "tab_connect_short": "Connect",
+ "tab_listen": "Listen",
+ "tab_listen_short": "Listen",
+ "fullscreen": "Full screen",
+ "exit_fullscreen": "Exit full screen",
+ "show_sessions": "Sessions",
+ "hide_sessions": "Back to terminal",
+ "sessions": "Sessions",
+ "refresh": "Refresh",
+ "name": "Session name",
+ "name_placeholder": "Ops shell",
+ "listen_mode": "Listen",
+ "destination_hash": "Destination hash",
+ "destination_placeholder": "7a55144adf826958a9529a3bcf08b149",
+ "allowed_hashes": "Allowed identity hashes",
+ "allowed_hashes_placeholder": "One hash per line",
+ "remote_command": "Remote command",
+ "default_command": "Default command",
+ "command_placeholder": "/bin/bash --login",
+ "mirror_exit_code": "Mirror exit code (-m)",
+ "no_id": "No local identity (-N)",
+ "no_auth": "Allow any identity (-n)",
+ "create_and_start": "Create and start",
+ "session_output": "Session output",
+ "no_command_yet": "No command started yet",
+ "start": "Start",
+ "stop": "Stop",
+ "clear": "Clear",
+ "remove": "Remove",
+ "send_line": "Send",
+ "command_input_placeholder": "Type command input...",
+ "status_running": "Running",
+ "status_stopped": "Stopped",
+ "status_failed": "Failed",
+ "no_sessions": "No sessions created yet.",
+ "unnamed_session": "Unnamed session",
+ "select_or_create_session": "Select or create a session to begin.",
+ "no_output_yet": "No output yet.",
+ "destination_required": "Destination hash is required for connect mode.",
+ "session_created": "Session created",
+ "session_started": "Session started",
+ "session_stopped": "Session stopped",
+ "session_removed": "Session removed",
+ "output_cleared": "Output cleared",
+ "title": "RNSH - Reticulum Remote Shell",
+ "description": "Manage persistent rnsh sessions",
+ "failed_to_load_sessions": "Failed to load RNSH sessions",
+ "failed_to_create_session": "Failed to create RNSH session",
+ "failed_to_start_session": "Failed to start RNSH session",
+ "failed_to_stop_session": "Failed to stop RNSH session",
+ "failed_to_remove_session": "Failed to remove RNSH session",
+ "failed_to_clear_output": "Failed to clear RNSH output",
+ "failed_to_send_input": "Failed to send RNSH input",
+ "listening_on": "Listening on",
+ "waiting_for_address": "Waiting for listener address...",
+ "copy_address": "Copy",
+ "address_copied": "Address copied to clipboard",
+ "failed_to_copy_address": "Failed to copy address",
+ "config_dir": "Reticulum config directory",
+ "config_dir_placeholder": "Defaults to the app's Reticulum config",
+ "config_dir_hint": "Leave blank to share the MeshChatX Reticulum instance. Override only to use a separate Reticulum config directory."
+ },
+ "rnx": {
+ "remote_exec": "Remote execution",
+ "usage_title": "Usage",
+ "usage_hint": "Run one-shot remote commands or start an rnx listener. Manage active sessions on the Sessions tab. Output streams while MeshChatX is running.",
+ "tab_sessions": "Sessions",
+ "tab_sessions_short": "Sessions",
+ "tab_execute": "Execute",
+ "tab_execute_short": "Execute",
+ "tab_listen": "Listen",
+ "tab_listen_short": "Listen",
+ "fullscreen": "Full screen",
+ "exit_fullscreen": "Exit full screen",
+ "show_sessions": "Sessions",
+ "hide_sessions": "Back to output",
+ "sessions": "Sessions",
+ "refresh": "Refresh",
+ "name": "Session name",
+ "name_placeholder": "Remote uptime",
+ "listen_mode": "Listen",
+ "destination_hash": "Listener destination hash",
+ "destination_placeholder": "7a55144adf826958a9529a3bcf08b149",
+ "allowed_hashes": "Allowed identity hashes",
+ "allowed_hashes_placeholder": "One hash per line",
+ "remote_command": "Command",
+ "command_placeholder": "uname -a",
+ "mirror_exit_code": "Mirror exit code (-m)",
+ "no_id": "No local identity (-N)",
+ "no_auth": "Allow any identity (-n)",
+ "detailed": "Detailed result (-d)",
+ "interactive": "Interactive mode (-x)",
+ "timeout": "Connect timeout (seconds)",
+ "result_timeout": "Result download timeout (seconds)",
+ "stdout_limit": "Stdout limit (bytes)",
+ "stderr_limit": "Stderr limit (bytes)",
+ "create_and_start": "Create and start",
+ "session_output": "Session output",
+ "no_command_yet": "No command started yet",
+ "start": "Start",
+ "stop": "Stop",
+ "clear": "Clear",
+ "remove": "Remove",
+ "send_line": "Send",
+ "command_input_placeholder": "Type interactive input...",
+ "status_running": "Running",
+ "status_stopped": "Stopped",
+ "status_failed": "Failed",
+ "no_sessions": "No sessions created yet.",
+ "unnamed_session": "Unnamed session",
+ "select_or_create_session": "Select or create a session to begin.",
+ "no_output_yet": "No output yet.",
+ "destination_required": "Destination hash is required for execute mode.",
+ "command_required": "A command is required for execute mode.",
+ "session_created": "Session created",
+ "session_started": "Session started",
+ "session_stopped": "Session stopped",
+ "session_removed": "Session removed",
+ "output_cleared": "Output cleared",
+ "title": "RNX - Reticulum Remote Execution",
+ "description": "Execute commands on remote systems over Reticulum",
+ "failed_to_load_sessions": "Failed to load RNX sessions",
+ "failed_to_create_session": "Failed to create RNX session",
+ "failed_to_start_session": "Failed to start RNX session",
+ "failed_to_stop_session": "Failed to stop RNX session",
+ "failed_to_remove_session": "Failed to remove RNX session",
+ "failed_to_clear_output": "Failed to clear RNX output",
+ "failed_to_send_input": "Failed to send RNX input",
+ "listening_on": "Listening on",
+ "waiting_for_address": "Waiting for listener address...",
+ "copy_address": "Copy",
+ "address_copied": "Address copied to clipboard",
+ "failed_to_copy_address": "Failed to copy address",
+ "config_dir": "Reticulum config directory",
+ "config_dir_placeholder": "Defaults to the app's Reticulum config",
+ "config_dir_hint": "Leave blank to share the MeshChatX Reticulum instance. Override only to use a separate Reticulum config directory."
+ },
+ "rnprobe": {
+ "network_diagnostics": "Network Diagnostics",
+ "title": "RNProbe - Destination Probe",
+ "description": "Send probes with configurable size to measure RTT and delivery.",
+ "destination_hash": "Destination Hash",
+ "full_destination_name": "Full Destination Name",
+ "probe_size_bytes": "Probe Size (bytes)",
+ "number_of_probes": "Number of Probes",
+ "wait_between_probes": "Wait Between Probes (seconds)",
+ "start_probe": "Start Probe",
+ "stop": "Stop",
+ "clear_results": "Clear Results",
+ "summary": "Summary",
+ "sent": "Sent",
+ "delivered": "Delivered",
+ "timeouts": "Timeouts",
+ "failed": "Failed",
+ "probe_results": "Probe Results",
+ "probe_responses_realtime": "Probe responses in real time",
+ "no_probes_yet": "No probes yet. Start a probe to test connectivity.",
+ "probe_number": "Probe #{number}",
+ "bytes": "bytes",
+ "hops": "Hops",
+ "rtt": "RTT",
+ "rssi": "RSSI",
+ "snr": "SNR",
+ "quality": "Quality",
+ "timeout": "Timeout",
+ "invalid_hash": "Invalid destination hash!",
+ "provide_full_name": "Please provide a full destination name!",
+ "failed_to_probe": "Failed to probe destination"
+ },
+ "rnstatus": {
+ "network_diagnostics": "Network Diagnostics",
+ "title": "RNStatus - Network Status",
+ "description": "Per-interface statistics and link status from your RNS instance.",
+ "refresh": "Refresh",
+ "include_link_stats": "Include Link Stats",
+ "sort_by": "Sort by:",
+ "none": "None",
+ "bitrate": "Bitrate",
+ "rx_bytes": "RX Bytes",
+ "tx_bytes": "TX Bytes",
+ "total_traffic": "Total Traffic",
+ "announces": "Announces",
+ "active_links": "Active Links: {count}",
+ "no_interfaces_found": "No interfaces found. Click refresh to load status.",
+ "mode": "Mode",
+ "rx_packets": "RX Packets",
+ "tx_packets": "TX Packets",
+ "clients": "Clients",
+ "peers_reachable": "reachable",
+ "noise_floor": "Noise Floor",
+ "interference": "Interference",
+ "cpu_load": "CPU Load",
+ "cpu_temp": "CPU Temp",
+ "memory_load": "Memory Load",
+ "battery": "Battery",
+ "network": "Network",
+ "incoming_announces": "Incoming Announces",
+ "outgoing_announces": "Outgoing Announces",
+ "airtime": "Airtime",
+ "channel_load": "Channel Load",
+ "blackhole_label": "Blackhole: {state}",
+ "blackhole_publishing": "Publishing",
+ "blackhole_inactive": "Inactive",
+ "failed_refresh": "Failed to refresh RNStatus",
+ "reloading": "Reloading RNS...",
+ "remote_query": "Remote query",
+ "remote_query_hint": "Query a remote transport instance that has remote management enabled and your management identity on its allow-list.",
+ "remote_transport_hash": "Remote transport identity hash",
+ "remote_transport_placeholder": "32-character hex hash",
+ "remote_timeout": "Timeout (seconds)",
+ "remote_active": "Showing remote status for {hash}",
+ "use_local": "Use local instance"
+ },
+ "remote_mgmt": {
+ "management_identity": "Management identity",
+ "select_identity": "Select identity file",
+ "create_identity": "Create",
+ "create_identity_prompt": "Name for the new management identity file",
+ "identity_created": "Management identity created",
+ "failed_load_identities": "Failed to load management identities",
+ "failed_create_identity": "Failed to create management identity"
+ },
+ "translator": {
+ "text_translation": "Text Translation",
+ "title": "Translator",
+ "description": "Translate text via a LibreTranslate server or local Argos Translate.",
+ "argos_translate": "Argos Translate",
+ "libretranslate": "LibreTranslate",
+ "api_server": "LibreTranslate API Server",
+ "api_server_description": "Enter the base URL of your LibreTranslate server (e.g., https://libretranslate.com or http://localhost:5000).",
+ "api_key_optional": "LibreTranslate API key (optional)",
+ "api_key_placeholder": "Leave empty unless your provider requires one",
+ "api_key_description": "If required, LibreTranslate expects this as api_key in the JSON translate body and optionally on the languages request. Omit for typical self-hosted instances.",
+ "source_language": "Source Language",
+ "auto_detect": "Auto-detect",
+ "target_language": "Target Language",
+ "select_target_language": "Select target language",
+ "argos_not_detected": "Argos Translate not detected",
+ "argos_not_detected_desc": "To use local translation, you must install the Argos Translate package using one of the following methods:",
+ "method_pip_venv": "Method 1: pip (venv)",
+ "method_pipx": "Method 2: pipx",
+ "note_restart_required": "Note: After installation, you may need to restart the application and install language packages via the Argos Translate CLI.",
+ "no_language_packages": "No language packages detected",
+ "no_language_packages_desc": "Argos Translate is installed but no language packages are available. Install language packages using one of the following commands:",
+ "install_all_languages": "Install all languages",
+ "install_specific_pair": "Install specific language pair (example: English to German)",
+ "after_install_note": "After installing language packages, click \"Refresh Languages\" to reload available languages.",
+ "text_to_translate": "Text to Translate",
+ "enter_text_placeholder": "Enter text to translate...",
+ "translate": "Translate",
+ "swap": "Swap",
+ "clear": "Clear",
+ "translation": "Translation",
+ "source": "Source",
+ "detected": "Detected",
+ "available_languages": "Available Languages",
+ "languages_loaded_from": "Languages are loaded from LibreTranslate API or Argos Translate packages.",
+ "refresh_languages": "Refresh Languages",
+ "failed_load_languages": "Failed to load languages. Make sure LibreTranslate is running or Argos Translate is installed.",
+ "failed_translate": "Translation failed. Make sure LibreTranslate is running or Argos Translate is installed.",
+ "select_languages_warning": "Please select both source and target languages.",
+ "auto_detect_not_supported": "Auto-detection is not supported with Argos Translate. Please select a source language.",
+ "copied_to_clipboard": "Copied to clipboard"
+ },
+ "call": {
+ "phone": "Phone",
+ "voicemail": "Voicemail",
+ "settings": "Settings",
+ "unknown": "Unknown",
+ "call_ended": "Call Ended",
+ "call_declined": "Call Declined",
+ "initiation": "Initiating...",
+ "send_to_voicemail": "Send to Voicemail",
+ "incoming_call": "Incoming Call...",
+ "busy": "Busy...",
+ "rejected": "Rejected...",
+ "calling": "Calling...",
+ "available": "Available...",
+ "ringing": "Ringing...",
+ "connecting": "Connecting...",
+ "establishing_link": "Establishing link...",
+ "connected": "Connected",
+ "accept": "Accept",
+ "decline": "Decline",
+ "hangup": "Hangup",
+ "answer_call": "Answer Call",
+ "decline_call": "Decline Call",
+ "hangup_call": "Hangup Call",
+ "mute_mic": "Mute Mic",
+ "unmute_mic": "Unmute Mic",
+ "mute_speaker": "Mute Speaker",
+ "unmute_speaker": "Unmute Speaker",
+ "telephone": "Telephone",
+ "enter_identity_hash_to_call": "Enter an identity to call.",
+ "identity_hash": "Identity Hash",
+ "call_history": "Call History",
+ "call_back": "Call Back",
+ "no_voicemails": "No Voicemails",
+ "voicemails_will_appear_here": "When people leave you messages, they'll show up here.",
+ "voicemail_inbox": "Voicemail Inbox",
+ "messages": "Messages",
+ "delete": "Delete",
+ "voicemail_settings": "Voicemail Settings",
+ "dependencies_missing": "Dependencies Missing",
+ "voicemail_requires_espeak": "Voicemail requires `espeak-ng` to generate greetings. Please install it on your system.",
+ "enable_voicemail": "Enable Voicemail",
+ "enable_voicemail_description": "Accept calls automatically and record messages",
+ "greeting_message": "Greeting Message",
+ "enter_greeting_text": "Enter greeting text...",
+ "greeting_text_description": "This text will be converted to speech using eSpeak NG.",
+ "tts_speed": "TTS Speed",
+ "tts_pitch": "TTS Pitch",
+ "tts_voice": "TTS Voice",
+ "tts_word_gap": "TTS Word Gap",
+ "save_and_generate": "Save & Generate",
+ "generating": "Generating...",
+ "preview_greeting": "Preview Greeting",
+ "stop_preview": "Stop Preview",
+ "answer_delay_seconds": "Answer Delay (s)",
+ "max_recording_seconds": "Max Recording (s)",
+ "recording_voicemail": "Recording Voicemail",
+ "call_status": "Call Status",
+ "call_recording": "Call Recorder",
+ "call_recording_description": "Record voice calls to storage",
+ "recordings": "Recordings",
+ "no_recordings": "No recordings found",
+ "remote_rx": "Remote (RX)",
+ "local_tx": "Local (TX)",
+ "download_rx": "Download RX",
+ "download_tx": "Download TX",
+ "minimize": "Minimize",
+ "active": "Active",
+ "default_quality": "Default Quality",
+ "default_duplex": "Default Duplex",
+ "full_duplex": "Full Duplex",
+ "half_duplex": "Half Duplex",
+ "switch_to_full_duplex": "Switch to full duplex",
+ "switch_to_half_duplex": "Switch to half duplex",
+ "ptt_hold_to_talk": "Hold to Talk",
+ "ptt_transmitting": "Transmitting",
+ "ptt_listening": "Listening",
+ "ptt_hold_hint": "Hold to unsquelch transmit on this half-duplex call",
+ "ptt_spacebar_hint": "Hold Space to talk",
+ "tx_packets": "TX Packets",
+ "rx_packets": "RX Packets",
+ "tx_data": "TX Data",
+ "rx_data": "RX Data",
+ "tx_rate": "TX Rate",
+ "rx_rate": "RX Rate",
+ "failed_to_switch_call_mode": "Failed to switch duplex mode",
+ "failed_to_set_ptt": "Failed to set push-to-talk",
+ "active_call": "Active Call",
+ "status": "Status",
+ "failed_to_answer_call": "Failed to answer call",
+ "failed_to_hangup_call": "Failed to hangup call",
+ "failed_to_toggle_microphone": "Failed to toggle microphone",
+ "failed_to_toggle_speaker": "Failed to toggle speaker",
+ "failed_to_switch_audio_profile": "Failed to switch audio profile",
+ "codec2_unavailable": "Codec2 is not available on this device. Low-bandwidth call profiles are hidden.",
+ "codec2_profile_remapped": "Codec2 is unavailable so the call profile was switched to Opus.",
+ "failed_to_initiate_call": "Failed to initiate call",
+ "enter_identity_hash_to_call_error": "Enter an identity to call",
+ "failed_to_save_settings": "Failed to save settings",
+ "do_not_disturb": "Do Not Disturb",
+ "load_more": "Load More",
+ "go_to_phone_page": "Go to Phone Page",
+ "search_history": "Search history...",
+ "allow_calls_from_contacts_only": "Only allow calls from contacts",
+ "settings_saved": "Settings saved",
+ "greeting_generated_successfully": "Greeting generated successfully",
+ "failed_to_generate_greeting": "Failed to generate greeting",
+ "no_greeting_audio_found": "No greeting audio found. Please generate one first.",
+ "voicemail_deleted": "Voicemail deleted",
+ "failed_to_delete_voicemail": "Failed to delete voicemail",
+ "ringtone": "Ringtone",
+ "ringtone_settings": "Ringtone Settings",
+ "enable_custom_ringtone": "Enable Custom Ringtone",
+ "enable_custom_ringtone_description": "Play a custom audio file for all incoming calls",
+ "upload_ringtone": "Upload Ringtone",
+ "uploading_ringtone": "Uploading Ringtone...",
+ "no_custom_ringtone_uploaded": "No custom ringtone uploaded",
+ "supports_audio_formats": "Supports MP3, OGG, WAV, M4A, FLAC. Will be converted to Opus.",
+ "preview_ringtone": "Preview Ringtone",
+ "remove_ringtone": "Remove Ringtone",
+ "ringtone_uploaded_successfully": "Ringtone uploaded successfully",
+ "failed_to_upload_ringtone": "Failed to upload ringtone",
+ "ringtone_deleted": "Ringtone deleted",
+ "failed_to_delete_ringtone": "Failed to delete ringtone",
+ "no_ringtone_audio_found": "No ringtone audio found. Please upload one first.",
+ "ringtone_volume": "Ringtone Volume",
+ "default_ringtone": "Default Ringtone",
+ "ringtone_for_non_contacts": "Ringtone for callers not in your contacts",
+ "primary_system_default": "Primary (System Default)",
+ "random": "Random",
+ "edit_contact": "Edit Contact",
+ "add_contact": "Add Contact",
+ "contact_name": "Contact Name",
+ "preferred_ringtone": "Preferred Ringtone",
+ "default_global_setting": "Default (Global Setting)",
+ "uploaded_ringtones": "Uploaded Ringtones",
+ "save_contact": "Save Contact",
+ "primary_ringtone_set": "Primary ringtone set",
+ "failed_to_set_primary_ringtone": "Failed to set primary ringtone",
+ "failed_to_update_ringtone_name": "Failed to update ringtone name",
+ "failed_to_play_ringtone": "Failed to play ringtone",
+ "failed_to_play_voicemail": "Failed to play voicemail",
+ "web_audio_not_available": "Web audio not available",
+ "no_audio_input_found": "No input audio device found. Check microphone and permissions.",
+ "microphone_permission_denied": "Microphone permission denied. Allow access and try again.",
+ "failed_to_update_dnd": "Failed to update Do Not Disturb status",
+ "failed_to_update_call_settings": "Failed to update call settings",
+ "failed_to_update_recording_status": "Failed to update call recording status",
+ "call_history_cleared": "Call history cleared",
+ "failed_to_clear_call_history": "Failed to clear call history",
+ "identity_banished": "Identity banished",
+ "failed_to_banish_identity": "Failed to banish identity",
+ "name_and_hash_required": "Name and identity hash required",
+ "contact_updated": "Contact updated",
+ "contact_added": "Contact added",
+ "contact_deleted": "Contact deleted",
+ "failed_to_delete_contact": "Failed to delete contact",
+ "hash_copied": "Hash copied to clipboard",
+ "failed_to_copy_hash": "Failed to copy hash",
+ "greeting_uploaded_successfully": "Greeting uploaded successfully",
+ "greeting_deleted": "Greeting deleted",
+ "failed_to_delete_greeting": "Failed to delete greeting",
+ "failed_to_start_recording_greeting": "Failed to start recording greeting",
+ "greeting_recorded_from_mic": "Greeting recorded from mic",
+ "failed_to_stop_recording_greeting": "Failed to stop recording greeting",
+ "failed_to_load_recording": "Failed to load recording audio",
+ "recording_deleted": "Recording deleted",
+ "failed_to_delete_recording": "Failed to delete recording",
+ "call_sent_to_voicemail": "Call sent to voicemail",
+ "failed_to_send_to_voicemail": "Failed to send call to voicemail",
+ "failed_load_audio_edit": "Failed to load audio for editing",
+ "ringtone_saved": "Ringtone saved successfully",
+ "failed_save_ringtone": "Failed to save edited ringtone"
+ },
+ "contacts": {
+ "title": "Contacts",
+ "description": "Manage your contact list and share/import identities.",
+ "search_placeholder": "Search contacts...",
+ "actions": "Actions",
+ "no_contacts": "No contacts found",
+ "load_more": "Load more",
+ "add_contact": "Add Contact",
+ "contact_name_optional": "Contact Name (Optional)",
+ "contact_name_placeholder": "e.g. Field Node",
+ "hash_or_uri": "LXMF Hash or URI",
+ "hash_or_uri_placeholder": "32-char hash, lxmf://..., or lxma://...",
+ "paste": "Paste",
+ "failed_load_contacts": "Failed to load contacts",
+ "invalid_contact_input": "Invalid input. Use a 32-char hash or lxma:// URI.",
+ "contact_already_exists": "Contact already exists",
+ "contact_added": "Contact added",
+ "failed_add_contact": "Failed to add contact",
+ "remove_contact": "Remove Contact",
+ "remove_contact_confirm": "Remove this contact?",
+ "contact_removed": "Contact removed",
+ "failed_remove_contact": "Failed to remove contact",
+ "share_contact": "Share Contact URI",
+ "copy_contact_uri": "Copy Contact URI",
+ "contact_uri_copied": "Contact URI copied to clipboard",
+ "failed_build_contact_uri": "Could not build a share URI for this contact",
+ "share_my_identity": "Share My Identity",
+ "identity_uri_copied": "Identity URI copied to clipboard",
+ "share": "Share",
+ "scan_qr": "Scan QR Code",
+ "scanner_hint": "Point camera at an identity QR code.",
+ "camera_not_supported": "Camera QR scanning is not supported in this browser.",
+ "camera_permission_denied": "Camera permission denied. Please allow camera access.",
+ "camera_not_found": "No camera device found.",
+ "camera_failed": "Failed to start camera scanner.",
+ "qr_scanned": "QR code scanned",
+ "importing_lxma": "Importing LXMA contact...",
+ "send_message": "Send Message",
+ "call_contact": "Call",
+ "export_contacts": "Export",
+ "import_contacts": "Import",
+ "export_success": "Contacts exported",
+ "export_failed": "Failed to export contacts",
+ "import_success": "Imported {added} contact(s)",
+ "import_failed": "Failed to import contacts",
+ "import_modal_title": "Import Contacts",
+ "import_file_hint": "Select a JSON file exported from Contacts.",
+ "edit_contact": "Edit Contact",
+ "enter_contact_name": "Enter contact name",
+ "contact_updated": "Contact updated",
+ "failed_update_contact": "Failed to update contact"
+ },
+ "android_storage": {
+ "setup_title": "Choose where MeshChatX stores data",
+ "setup_desc": "File-manager storage is recommended. You can browse and back up your identity and messages under Android/data/com.meshchatx/files/. The app restarts when you change this during setup.",
+ "setup_external_title": "File-manager storage (recommended)",
+ "setup_external_desc": "Android/data/com.meshchatx/files/meshchatx, visible in many file managers.",
+ "setup_internal_title": "Private app storage",
+ "setup_internal_desc": "Hidden from file managers. Only this app can access it.",
+ "setup_continue": "Continue",
+ "upgrade_title": "Move data to file-manager storage?",
+ "upgrade_desc": "Your MeshChatX data is in private app storage. Copy it to the file-manager folder so you can browse and back up files, then restart the app.",
+ "upgrade_copy": "Copy and restart",
+ "upgrade_stay_internal": "Stay on private storage",
+ "working": "Preparing…",
+ "copy_restart_hint": "Copy scheduled. Closing the app. Open MeshChatX again to finish.",
+ "restart_to_apply": "Restart the app to apply your storage choice.",
+ "failed": "Could not update storage location."
+ },
+ "tutorial": {
+ "title": "Getting Started",
+ "welcome": "Welcome to",
+ "welcome_desc": "The future of off-grid communication. Secure, decentralized, and unstoppable.",
+ "migration_title": "Earlier install folder detected",
+ "migration_desc": "Data was found in .reticulum-meshchat. Copy it into .reticulum-meshchatx and restart, or start fresh and leave the old folder as-is.",
+ "migration_migrate": "Copy to .reticulum-meshchatx",
+ "migration_fresh": "Start fresh",
+ "migration_working": "Updating storage…",
+ "migration_done_restart": "Restart the app to use the new folder.",
+ "migration_failed": "Could not update storage.",
+ "security": "Security & Performance",
+ "security_desc": "Massive improvements in speed, security, and crash recovery.",
+ "security_desc_page": "Massive improvements in speed, security, and integrity with built-in crash recovery.",
+ "maps": "Maps",
+ "maps_desc": "OpenLayers w/ MBTiles support (export and offline support) and custom API endpoints.",
+ "maps_desc_page": "OpenLayers support with offline MBTiles (export and offline support) and custom API endpoints for online maps.",
+ "voice": "Full LXST Voice",
+ "voice_desc": "Voicemail, ringtones, phonebook, and contact sharing.",
+ "voice_desc_page": "Crystal clear voice calls over mesh. Voicemail, custom ringtones, and phonebook discovery.",
+ "tools": "Built-in extras",
+ "tools_desc": "Micron Editor, NomadNet, RNS tools, Docs.",
+ "tools_desc_page": "Micron editor, NomadNet nodes, RNS tools, and integrated documentation.",
+ "archiver": "Archiver",
+ "archiver_desc": "Manual and Automated page archiving.",
+ "archiver_desc_page": "Manual and Automated page archiving tools for offline browsing.",
+ "banishment": "Banishment",
+ "banishment_desc": "Banish annoying people, nodes and support for RNS blackhole level dropping their announces",
+ "palette": "Command Palette + Keybindings",
+ "palette_desc": "Navigate everything instantly and customize shortcuts.",
+ "palette_desc_page": "Navigate the entire application and customize your workflow with instant shortcuts.",
+ "i18n": "i18n Support",
+ "i18n_desc": "Available in English, German, Italian, Russian, Dutch, French, and Spanish.",
+ "i18n_desc_page": "Full internationalization support for English, German, Italian, Russian, Dutch, French, and Spanish.",
+ "more_features": "+ many more features!",
+ "connect": "Connect to the Mesh",
+ "connect_desc": "Choose how you want to connect. Discovery and bootstrap nodes can be configured later on the Interfaces page.",
+ "connect_desc_page": "Use local LAN discovery or configure custom interfaces from the Interfaces page. Auto-discovery and bootstrap nodes are available there for advanced setup.",
+ "identity_title": "Identity",
+ "identity_desc": "Choose a new identity or import an existing key before connecting.",
+ "identity_desc_page": "Choose a new identity or import an existing key before connecting to the mesh.",
+ "identity_new": "New Identity (default)",
+ "identity_new_desc": "Keep the default identity and optionally set your display name.",
+ "identity_import": "Import existing identity",
+ "identity_import_desc": "Import from an identity file or paste a base32 key.",
+ "identity_set_name": "Set name",
+ "identity_upload_file": "Import identity file",
+ "identity_base32_placeholder": "Paste base32 private identity key",
+ "identity_import_required": "Choose an identity file or paste a base32 key to import.",
+ "identity_import_failed": "Failed to import identity",
+ "identity_import_empty_file": "Identity file is empty.",
+ "identity_import_file_too_large": "Identity file is too large.",
+ "identity_import_key_only_hint": "This imports your identity key only. To restore messages and settings, use About → Restore from File with a database backup zip.",
+ "identity_file_overrides_base32": "A selected file will be used instead of the pasted base32 key.",
+ "identity_import_pending_activate": "You imported an identity but have not activated it yet. Activate it now?",
+ "identity_import_pending_kept": "Imported identity was saved but not activated. Switch to it later from Identities.",
+ "identity_default_delete_failed": "Imported identity is active, but the default identity could not be removed.",
+ "identity_name_update_failed": "Failed to set display name",
+ "identity_switch_failed": "Failed to activate imported identity",
+ "suggested_networks": "Suggested Public Networks",
+ "suggested_relays": "Suggested Relays",
+ "use": "Use",
+ "online": "Online",
+ "custom_interfaces": "Custom Interfaces",
+ "custom_interfaces_desc": "Want to add a bespoke interface? Head to the Interfaces page to configure a custom connection.",
+ "custom_interfaces_desc_page": "For any private relays or hardware you manage yourself, add them through the dedicated Interfaces page.",
+ "open_interfaces": "Open Interfaces page",
+ "learn_create": "Learn & Create",
+ "learn_create_desc": "Discover how to use MeshChatX to its full potential.",
+ "learn_create_desc_page": "Discover how to use MeshChatX to its full potential and create your own content.",
+ "learn_create_more": "More to explore",
+ "documentation": "Documentation",
+ "documentation_desc": "Read the official MeshChatX and Reticulum documentation.",
+ "documentation_desc_page": "Guides for MeshChatX and the Reticulum stack.",
+ "meshchatx_docs": "MeshChatX Docs",
+ "reticulum_docs": "Reticulum Docs",
+ "read_meshchatx_docs": "Read MeshChatX Docs",
+ "reticulum_manual": "Reticulum Network Manual",
+ "micron_editor": "Micron Editor",
+ "micron_editor_desc": "Take a look at the Micron Editor for a guide on creating mesh-native pages.",
+ "micron_editor_desc_page": "Learn how to create mesh-native pages and interactive content using the Micron markup language.",
+ "open_micron_editor": "Open Micron Editor",
+ "open_mesh_server": "Open Mesh Server",
+ "open_identities": "Identities",
+ "identities_card_title": "Multiple identities",
+ "identities_card_desc": "Switch personas, export keys, and keep separate profiles on one device.",
+ "identities_card_desc_page": "Run multiple Reticulum identities on one install: create and delete personas, switch who you are on the mesh, and export keys when you need them.",
+ "paper_messages": "Explore NomadNet Nodes",
+ "paper_messages_desc": "Browse Nomad pages and services.",
+ "send_messages": "Send Messages",
+ "send_messages_desc": "Start a secure conversation.",
+ "explore_nodes": "Visualize Nodes",
+ "explore_nodes_desc": "Open the map or network graph.",
+ "voice_calls": "Voice Calls",
+ "voice_calls_desc": "Place and receive voice calls.",
+ "ready": "Ready to Roll!",
+ "ready_desc": "Everything is set up. Reticulum has been restarted with your new configuration.",
+ "ready_desc_page": "MeshChatX is now configured and Reticulum has been hot-reloaded with your new settings.",
+ "privacy_title": "Privacy & security",
+ "privacy_desc": "Optional hardening for this device. You can change these any time in Settings → Privacy.",
+ "privacy_later_hint": "Recommended for sensitive use. Leaving options off is fine for casual installs.",
+ "docker_note": "Reticulum was restarted automatically. If anything looks off, you can restart MeshChatX from the menu.",
+ "restart_required": "Reticulum Restarted",
+ "restart_desc_page": "Reticulum was hot-reloaded with your new configuration. No manual restart is required for normal use.",
+ "back": "Back",
+ "skip": "Skip",
+ "next": "Next",
+ "restart_start": "Start MeshChatX",
+ "finish_setup": "Finish Setup",
+ "skip_setup": "Skip Setup",
+ "continue": "Continue",
+ "skip_confirm": "Are you sure you want to skip the setup? You'll need to manually add interfaces later.",
+ "discovery_question": "Do you want to use community interface discovering and auto-connect?",
+ "discovery_desc": "This allows MeshChatX to automatically find and connect to public community nodes near you or on the internet.",
+ "yes": "Yes, use discovery",
+ "no": "No, manual setup",
+ "discovery_enabled": "Community discovery enabled",
+ "failed_enable_discovery": "Failed to enable discovery",
+ "mode_discovery_title": "Auto-Discovery",
+ "mode_discovery_desc": "Find and auto-connect to nearby and internet-reachable Reticulum nodes. You'll pick one or more bootstrap nodes next.",
+ "mode_local_title": "Local Network Only",
+ "mode_local_desc": "Use Reticulum on your LAN/Wi-Fi only. Adds an AutoInterface for zero-config local mesh.",
+ "mode_manual_title": "Manual Setup",
+ "mode_manual_desc": "Skip for now and configure interfaces, discovery, and bootstrap nodes from the Interfaces page later.",
+ "mode_change_later": "You can change connection settings any time from Reticulum settings.",
+ "bootstrap_title": "Pick Bootstrap Nodes",
+ "bootstrap_desc": "Bootstrap nodes give discovery a starting point. Select one or more, then continue.",
+ "bootstrap_pick_random_tcp": "Pick 4 random TCP nodes",
+ "bootstrap_pick_random_none": "No suitable TCP community presets are available. Refresh community presets or pick nodes manually (discovered lists are not used for random pick).",
+ "bootstrap_pick_random_done": "Selected {count}: {names}",
+ "bootstrap_selected_nodes_heading": "Selected nodes",
+ "bootstrap_search_placeholder": "Search discovered and community…",
+ "bootstrap_search_no_match": "No nodes match your search",
+ "bootstrap_search_clear": "Clear search",
+ "bootstrap_only_label": "Bootstrap-only for added interfaces",
+ "bootstrap_only_hint": "When on, every interface you add in this step is created as bootstrap-only, which Reticulum can drop after enough links auto-connect. The default is adjustable in Interfaces, Discovery settings.",
+ "bootstrap_desc_page": "Bootstrap nodes give Reticulum a starting point so it can discover the rest of the network. Select one or more from discovered or community nodes below.",
+ "bootstrap_discovered": "Discovered Nodes",
+ "bootstrap_community": "Community Nodes",
+ "bootstrap_selected": "{count} selected",
+ "bootstrap_skip": "Skip for now",
+ "bootstrap_confirm": "Add selected and restart RNS",
+ "bootstrap_added": "Added {count} bootstrap interface(s)",
+ "bootstrap_pick_at_least_one": "Pick at least one bootstrap node",
+ "failed_add_bootstrap": "Failed to add bootstrap interface",
+ "failed_add_bootstrap_none": "No bootstrap interfaces were added. Check your selection and try again.",
+ "failed_save_bootstrap_only": "Failed to save bootstrap-only preference",
+ "failed_enable_propagation": "Failed to enable auto-propagation",
+ "connect_mode_required": "Choose a connection mode to continue",
+ "local_added": "Local AutoInterface added",
+ "failed_add_local": "Failed to add local interface",
+ "failed_reload_rns": "Failed to restart Reticulum",
+ "ready_finished": "Reticulum has been restarted with your new configuration",
+ "propagation": "Propagation Node",
+ "propagation_question": "Do you want to auto-configure propagation mode?",
+ "propagation_desc": "Propagation nodes act like an always-on mailbox for your messages. They securely store messages while you are offline and deliver them as soon as you reconnect to the network.",
+ "propagation_auto": "Auto-configure will automatically find and connect to the most reliable propagation node available. If no node is currently available, MeshChatX will wait for one to appear. Until then, offline message syncing will be unavailable.",
+ "propagation_manual": "Or manually configure?",
+ "propagation_manual_desc": "You can manually configure a propagation node later in settings, or leave it empty if you don't want to use one.",
+ "propagation_enable_auto": "Enable Auto-Configuration",
+ "propagation_skip_auto": "Configure Manually / Skip",
+ "auto_propagation_enabled": "Auto-propagation enabled"
+ },
+ "command_palette": {
+ "search_placeholder": "Search commands, routes, or peers",
+ "no_results": "No results found for \"{query}\"",
+ "group_actions": "Actions",
+ "group_recent": "Recent Peers",
+ "group_contacts": "Contacts",
+ "footer_navigate": "Navigate",
+ "footer_select": "Select",
+ "nav_messages": "Messages",
+ "nav_messages_desc": "Conversations and LXMF inbox",
+ "nav_nomad": "Nomad Network",
+ "nav_nomad_desc": "Browse Nomad pages and services",
+ "nav_map": "Map",
+ "nav_map_desc": "Geographic map and layers",
+ "nav_paper": "Paper Messages",
+ "nav_paper_desc": "Signed paper messages (QR)",
+ "nav_call": "Voice Calls",
+ "nav_call_desc": "Voice calls over the mesh",
+ "nav_settings": "Settings",
+ "nav_settings_desc": "App and transport preferences",
+ "nav_ping": "Ping",
+ "nav_ping_desc": "Measure RTT to a destination",
+ "nav_rnprobe": "RNProbe",
+ "nav_rnprobe_desc": "Probe reachability and delay",
+ "nav_rncp": "RNCP",
+ "nav_rncp_desc": "File copy over Reticulum (RNCP)",
+ "nav_rns_filesync": "File Sync",
+ "nav_rns_filesync_desc": "Shared folders over the mesh",
+ "nav_rnsh": "RNSH",
+ "nav_rnsh_desc": "Remote shell over Reticulum",
+ "nav_rnx": "RNX",
+ "nav_rnx_desc": "Remote command execution over Reticulum",
+ "nav_rnstatus": "RNStatus",
+ "nav_rnstatus_desc": "Interface and link statistics",
+ "nav_rnpath": "RNPath",
+ "nav_rnpath_desc": "Paths and announce queues",
+ "nav_rnpath_trace": "RNPath Trace",
+ "nav_rnpath_trace_desc": "Hop list toward a hash",
+ "nav_translator": "Translator",
+ "nav_translator_desc": "Translate message text",
+ "nav_forwarder": "Forwarder",
+ "nav_forwarder_desc": "LXMF forwarding rules",
+ "nav_documentation": "Documentation",
+ "nav_documentation_desc": "Offline manuals",
+ "nav_repository_server": "Repository server",
+ "nav_repository_server_desc": "MeshChatX wheels, Reticulum packages, and your files over HTTP",
+ "nav_micron_editor": "Micron Editor",
+ "nav_micron_editor_desc": "Edit Micron markup",
+ "nav_reticulum_config_editor": "Reticulum Config Editor",
+ "nav_reticulum_config_editor_desc": "Edit the raw Reticulum config file",
+ "nav_rnode_flasher": "RNode Flasher",
+ "nav_rnode_flasher_desc": "Device firmware update",
+ "nav_debug_logs": "Debug Logs",
+ "nav_debug_logs_desc": "Application and access logs",
+ "action_sync": "Sync Messages",
+ "action_sync_desc": "Sync with your propagation node",
+ "action_compose": "Compose Message",
+ "action_compose_desc": "New LXMF message",
+ "action_getting_started": "Getting Started",
+ "action_getting_started_desc": "Open the setup guide",
+ "action_changelog": "Changelog",
+ "action_changelog_desc": "Recent changes"
+ },
+ "post_install": {
+ "windows_screen_security_title": "Enable Windows screen security?",
+ "windows_screen_security_desc": "MeshChatX can use Windows content protection (the same DRM-style display flag media apps use) so screenshots, recorders, and Windows Recall cannot capture the app window. You can change this later under Settings → Privacy.",
+ "windows_screen_security_enable": "Enable screen security",
+ "windows_screen_security_later": "Not now"
+ }
}
diff --git a/meshchatx/src/frontend/locales/es.json b/meshchatx/src/frontend/locales/es.json
index 58d3b04b..f5f1eafc 100644
--- a/meshchatx/src/frontend/locales/es.json
+++ b/meshchatx/src/frontend/locales/es.json
@@ -433,8 +433,8 @@
"map_tiles_heading": "Teselas y sin conexión",
"map_overlay_limits_heading": "Límites de superposiciones remotas",
"map_overlay_limits_desc": "Límites de protección para importaciones KMZ/KML/GeoJSON desde NomadNet y RNGit. Los valores se restringen a rangos seguros.",
- "map_overlay_max_bytes": "Tamaño máx. del archivo de superposición (bytes)",
- "map_overlay_max_features": "Elementos máx. por superposición",
+ "map_overlay_max_bytes": "Tamaño máx. Del archivo de superposición (bytes)",
+ "map_overlay_max_features": "Elementos máx. Por superposición",
"map_overlay_max_kmz_uncompressed_bytes": "Tamaño máx. KMZ sin comprimir (bytes)",
"map_overlay_max_sources": "Fuentes de superposición máx.",
"map_overlay_max_concurrent_jobs": "Trabajos de superposición concurrentes máx.",
@@ -486,7 +486,7 @@
"copy_rpc_config": "Copiar configuración RPC",
"connected_to_shared_instance": "Este proceso de MeshChatX está conectado a una instancia externa compartida de Reticulum. Edite la configuración de esa instancia para cambiar los ajustes de compartir/RPC.",
"copy_failed": "Error al copiar al portapapeles",
- "rpc_key_hidden": "Oculta — toca para mostrar",
+ "rpc_key_hidden": "Oculta. Toca para mostrar",
"rpc_key_show": "Mostrar clave RPC",
"rpc_key_hide": "Ocultar clave RPC",
"refresh_community_interfaces": "Actualizar desde directory.rns.recipes",
@@ -1205,7 +1205,7 @@
"memory_pressure_relaxed": "Active (low host RAM)",
"memory_pressure_paths": "{count} rutas rastreadas",
"env_host_battery": "Bateria del host",
- "app_battery_use": "Bat. est. MeshChatX",
+ "app_battery_use": "Bat. Est. MeshChatX",
"app_battery_use_hint": "Estimacion por tiempo de CPU de MeshChatX desde el inicio. No es atribucion del SO.",
"app_battery_use_warming": "Midiendo…",
"app_battery_use_rate": "~{rate}",
@@ -1415,8 +1415,8 @@
"tile_connectivity_retry": "Reintentar mosaicos",
"tile_connectivity_use_offline": "Modo sin conexión",
"tile_connectivity_dismiss": "Cerrar",
- "tile_failover_trying": "Falló la carga de mosaicos; probando {provider}…",
- "tile_failover_offline": "Mosaicos en línea no disponibles; cambiando a modo sin conexión",
+ "tile_failover_trying": "Falló la carga de mosaicos. Probando {provider}…",
+ "tile_failover_offline": "Mosaicos en línea no disponibles. Cambiando a modo sin conexión",
"search_no_results": "No se han encontrado resultados",
"custom_tile_server_unavailable": "El servidor de baldosas personalizado no es accesible en modo offline",
"custom_nominatim_unavailable": "Nominatim servidor personalizado no es accesible en modo offline",
@@ -1521,7 +1521,7 @@
"remote_overlays_kind": "Tipo de origen",
"remote_overlays_url": "URL de origen",
"remote_overlays_paths": "Rutas de archivos del repositorio (una por línea)",
- "remote_overlays_ref": "Ref. de Git (rama, etiqueta o commit)",
+ "remote_overlays_ref": "Ref. De Git (rama, etiqueta o commit)",
"remote_overlays_refresh_interval": "Intervalo de actualización automática (segundos, 0 = desactivado)",
"remote_overlays_import": "Importar / obtener",
"remote_overlays_importing": "Obteniendo…",
@@ -1593,7 +1593,7 @@
"snr": "SNR{snr}",
"stamp_cost": "Costo de sello{cost}",
"stamp_ticket_valid": "Boleto de sello saliente activo (envío instantáneo). Caduca {expires}.",
- "stamp_ticket_expired": "Boleto de sello caducado; el próximo mensaje puede requerir prueba de trabajo.",
+ "stamp_ticket_expired": "Boleto de sello caducado. El próximo mensaje puede requerir prueba de trabajo.",
"pop_out_chat": "Apágalo.",
"open_in_split": "Abrir en vista dividida",
"select_conversation_for_pane": "Selecciona una conversación para abrirla aquí",
@@ -1900,7 +1900,7 @@
"micron_wasm_update_source_upload": "Archivo local (sin verificar)",
"micron_wasm_update_active_size": "Tamaño: {bytes} bytes",
"micron_wasm_update_bundled_only": "Se usa el WASM incluido con la aplicación. No se descarga nada de la red hasta que use las acciones siguientes.",
- "micron_wasm_update_isolation_note": "El navegador no puede aislar por completo WebAssembly de JavaScript. Esta aplicación solo expone la conversión Micron mediante una API limitada; el HTML generado sigue saneándose. wasm_exec.js siempre proviene del paquete de la aplicación para coincidir con el runtime de Go.",
+ "micron_wasm_update_isolation_note": "El navegador no puede aislar por completo WebAssembly de JavaScript. Esta aplicación solo expone la conversión Micron mediante una API limitada. El HTML generado sigue saneándose. Wasm_exec.js siempre proviene del paquete de la aplicación para coincidir con el runtime de Go.",
"micron_wasm_update_github_heading": "Instalar desde versión de GitHub",
"micron_wasm_update_github_url_hint": "Pulse Obtener para que el servidor MeshChat descargue desde GitHub (el navegador sigue en el mismo origen por la CSP). Ruta de origen:",
"micron_wasm_update_fetch_github": "Obtener e instalar",
@@ -1954,7 +1954,7 @@
"reduce_background_polling": "Slow background status polling",
"reduce_background_polling_desc": "Multiplies App shell poll intervals (calls, unread, app info).",
"background_poll_multiplier": "Background poll multiplier",
- "background_poll_multiplier_desc": "2–10. Applied to App shell intervals when the option above is on.",
+ "background_poll_multiplier_desc": "2-10. Applied to App shell intervals when the option above is on.",
"reduce_interfaces_discovery": "Slow Interfaces page polls",
"reduce_interfaces_discovery_desc": "Uses the intervals below for stats and discovered-interface refresh.",
"interfaces_stats_poll_seconds": "Interfaces stats poll (seconds)",
@@ -3282,7 +3282,7 @@
"contact_name_optional": "Nombre de contacto (opcional)",
"contact_name_placeholder": "por ejemplo, Nodo de Campo",
"hash_or_uri": "LXMF Hash o URI",
- "hash_or_uri_placeholder": "hash de 32 caracteres, lxmf://... o lxma://...",
+ "hash_or_uri_placeholder": "hash de 32 caracteres, lxmf://... O lxma://...",
"paste": "Pegar",
"failed_load_contacts": "No se pudieron cargar los contactos",
"invalid_contact_input": "Invalida entrada. Use a 32-char hash or lxma:// URI.",
diff --git a/meshchatx/src/frontend/locales/fi.json b/meshchatx/src/frontend/locales/fi.json
index 65c59478..9b179cc6 100644
--- a/meshchatx/src/frontend/locales/fi.json
+++ b/meshchatx/src/frontend/locales/fi.json
@@ -21,7 +21,7 @@
"bot_stopped": "Botti pysäytetty",
"start_pending": "Käynnistetään bottia... LXMF:llä ja Reticulumilla menee hetki.",
"stop_pending": "Pysäytetään bottia",
- "restart_pending": "Pysäytetään bottia... tähän voi mennä hetki.",
+ "restart_pending": "Pysäytetään bottia... Tähän voi mennä hetki.",
"bot_ready": "Botti on käynnissä",
"failed_to_start": "Botin käynnistys epäonnistui",
"failed_to_stop": "Botin pysäytys epäonnistui",
@@ -228,7 +228,7 @@
"run_local_node": "Ylläpidä paikallista levityssolmua",
"run_local_node_description": "MeshChatX kuuluttaa ja ylläpitää solmua tämän tiivisteen kohteessa.",
"preferred_propagation_node": "Suosittu levityssolmu",
- "preferred_node_placeholder": "Kohteen tiiviste, esim. a39610c89d18bb48c73e429582423c24",
+ "preferred_node_placeholder": "Kohteen tiiviste, esim. A39610c89d18bb48c73e429582423c24",
"fallback_node_description": "Viestit palautuvat tähän solmuun, kun suora toimitus epäonnistuu.",
"auto_select_node": "Valitse paras solmu automaattisesti",
"auto_select_node_description": "Etsi ja valitse automaattisesti lähin saatavilla oleva levityssolmu.",
@@ -403,7 +403,7 @@
"csp_settings": "Sisällön turvallisuuskäytänteet (CSP)",
"csp_description": "Lisää käsin sallittuja verkkotunnuksia eri lähteille. Tätä voi käyttää karttapalveluille, boteille tai ulkoisille liitännäisille.",
"csp_extra_connect_src": "Ylimääräiset yhteyslähteet",
- "csp_extra_connect_src_description": "Lisälähteet nouto-/websocket-yhteyksille (e.g. https://api.example.com).",
+ "csp_extra_connect_src_description": "Lisälähteet nouto-/websocket-yhteyksille (e.g. Https://api.example.com).",
"csp_extra_img_src": "Lisälähteet kuville",
"csp_extra_img_src_description": "Kuvien ja karttalaattojen lisälähteet.",
"csp_extra_frame_src": "Iframe-lisälähteet",
@@ -486,7 +486,7 @@
"copy_rpc_config": "Kopioi RPC-asetukset",
"connected_to_shared_instance": "Tämä MeshChatX-prosessi on liitetty ulkoiseen jaettuun Reticulum-instanssiin. Muokkaa kyseisen instanssin asetuksia muuttaaksesi jako-/RPC-asetuksia.",
"copy_failed": "Kopiointi leikepöydälle epäonnistui",
- "rpc_key_hidden": "Piilotettu — napauta näyttääksesi",
+ "rpc_key_hidden": "Piilotettu. Napauta näyttääksesi",
"rpc_key_show": "Näytä RPC-avain",
"rpc_key_hide": "Piilota RPC-avain",
"refresh_community_interfaces": "Päivitä lähteestä directory.rns.recipes",
@@ -613,7 +613,7 @@
"title_label": "Nimike",
"title_placeholder": "Minun arkkini",
"short_name_label": "Lyhyt nimi",
- "short_name_placeholder": "esim. kissat",
+ "short_name_placeholder": "esim. Kissat",
"description_label": "Kuvaus",
"type_label": "Tyyppi",
"type_static": "Staattinen",
@@ -1415,8 +1415,8 @@
"tile_connectivity_retry": "Hae laatat uudelleen",
"tile_connectivity_use_offline": "Käytä yhteydetöntä tilaa",
"tile_connectivity_dismiss": "Hylkää",
- "tile_failover_trying": "Karttalaattojen lataus epäonnistui; yritetään {provider}…",
- "tile_failover_offline": "Verkossa olevat laatat eivät ole saatavilla; vaihdetaan yhteydettömään tilaan",
+ "tile_failover_trying": "Karttalaattojen lataus epäonnistui. Yritetään {provider}…",
+ "tile_failover_offline": "Verkossa olevat laatat eivät ole saatavilla. Vaihdetaan yhteydettömään tilaan",
"search_no_results": "Ei tuloksia",
"custom_tile_server_unavailable": "Mukautettu laattapalvelin ei ole saatavilla yhteydettömässä tilassa",
"custom_nominatim_unavailable": "Mukautettu Nominatim-palvelin ei ole saatavilla yhteydettömässä tilassa",
@@ -1593,7 +1593,7 @@
"snr": "SNR {snr}",
"stamp_cost": "Leiman hinta {cost}",
"stamp_ticket_valid": "Lähtevien viestien merkintä on voimassa (toimitus heti). Vanhenee {expires}.",
- "stamp_ticket_expired": "Leimamerkintä on vanhentunut; seuraava viesti voi vaatia PoW-työtä.",
+ "stamp_ticket_expired": "Leimamerkintä on vanhentunut. Seuraava viesti voi vaatia PoW-työtä.",
"pop_out_chat": "Irrota keskustelu",
"open_in_split": "Avaa jaetussa näkymässä",
"select_conversation_for_pane": "Valitse keskustelu avataksesi sen tässä",
@@ -1704,7 +1704,7 @@
"folders_imported": "Kansiot tuotu",
"failed_import_folders": "Kansioiden tuonti epäonnistui",
"failed_read_clipboard": "Leikepöydän lukeminen epäonnistui",
- "clipboard_read_requires_secure_context": "Leikepöytä luetaan vain suojatuilla sivuilla (https or http://127.0.0.1). http://0.0.0.0 tai muu ei-paikallinen HTTP lähde estää leikepöydän rajapinnan — avaa sovellus osoitteessa 127.0.0.1 tai käyttäen https-protokollaa, tai liitä painamalla Ctrl+V.",
+ "clipboard_read_requires_secure_context": "Leikepöytä luetaan vain suojatuilla sivuilla (https or http://127.0.0.1). http://0.0.0.0 tai muu ei-paikallinen HTTP lähde estää leikepöydän rajapinnan. Avaa sovellus osoitteessa 127.0.0.1 tai käyttäen https-protokollaa, tai liitä painamalla Ctrl+V.",
"clipboard_write_unavailable": "Automaattinen kopiointi epäonnistui. Avaa https-protokollalla tai osoitteessa http://127.0.0.1, tai valitse teksti ja kopioi se käsin.",
"oversized_body_notice": "Viesti on liian pitkä näytettäväksi tässä ({count} merkkiä). Kopioi koko teksti luettavaksi tai muokattavaksi muualla.",
"oversized_body_copy": "Kopioi koko teksti",
@@ -1766,7 +1766,7 @@
"failed_start_recording": "tallennuksen aloitus epäonnistui",
"failed_start_recording_help_mediadevices": "Rajapintaa mikrofonin käyttöön ei ole saatavilla tässä verkkonäkymässä.",
"failed_start_recording_help_web_audio": "Web Audio ei ole saatavilla tällä selaimella.",
- "failed_start_recording_help_audio_worklet": "AudioWorklet ei ole tuettu — OPUS-ääniviestit vaativat sen.",
+ "failed_start_recording_help_audio_worklet": "AudioWorklet ei ole tuettu. OPUS-ääniviestit vaativat sen.",
"failed_start_recording_help_permission": "Salli mikrofonin käyttö, tai yritä kun toinen sovellus vapauttaa mikrofonin käytön.",
"remove_audio_confirm": "Haluatko varmasti poistaa tämän ääniliitteen?",
"failed_generate_qr": "QR-koodin luonti epäonnistui",
@@ -1916,14 +1916,14 @@
"micron_wasm_update_source_upload": "Paikallinen tiedosto (todentamaton)",
"micron_wasm_update_active_size": "Koko: {bytes} tavua",
"micron_wasm_update_bundled_only": "Käytetään sovelluksen sisäänrakennettua WASMia. Verkosta ei noudeta mitään, jos et käytä alla olevia toimintoja.",
- "micron_wasm_update_isolation_note": "Selain ei pysty täysin eristämään WebAssemlyä JavaScriptistä. Sovellus paljastaa Micron-muunnoksen vain ohuelti; HTML-koodi siistitään joka tapauksessa. wasm_exec.js käytetään aina sovelluspaketista, jotta se on yhtenevä Go-runtimekomponentin kanssa.",
+ "micron_wasm_update_isolation_note": "Selain ei pysty täysin eristämään WebAssemlyä JavaScriptistä. Sovellus paljastaa Micron-muunnoksen vain ohuelti. HTML-koodi siistitään joka tapauksessa. Wasm_exec.js käytetään aina sovelluspaketista, jotta se on yhtenevä Go-runtimekomponentin kanssa.",
"micron_wasm_update_github_heading": "Asenna GitHub-julkaisu",
"micron_wasm_update_github_url_hint": "Napauta Nouda niin MeshChat-taustapalvelin lataa GitHubista (selaimen lähde pysyy samana sisällön suojauskäytännön (CSP) vuoksi). Lähteen polku:",
"micron_wasm_update_fetch_github": "Nouda ja asenna",
"micron_wasm_update_upload_heading": "Asenna tiedostosta",
"micron_wasm_update_upload_warning": "Ladattu WASM ei ole todennettu. Käytä vain luotettuja tiedostoja.",
"micron_wasm_update_revert_bundled": "Poista ohitus (käytä sovelluspaketin WASMia)",
- "micron_wasm_update_err_empty_tag": "Anna julkaisun tagi (esim. v1.0.5).",
+ "micron_wasm_update_err_empty_tag": "Anna julkaisun tagi (esim. V1.0.5).",
"micron_wasm_update_err_not_wasm": "Valitse tiedosto, jonka pääte on .wasm.",
"micron_wasm_update_err_too_large": "Tiedosto on liian suuri käytettäväksi ajonaikaiseen ohitukseen.",
"micron_wasm_update_err_too_small": "Tiedosto on liian pieni ollakseen kelvollinen WASM-moduuli.",
@@ -1954,7 +1954,7 @@
"reduce_background_polling": "Slow background status polling",
"reduce_background_polling_desc": "Multiplies App shell poll intervals (calls, unread, app info).",
"background_poll_multiplier": "Background poll multiplier",
- "background_poll_multiplier_desc": "2–10. Applied to App shell intervals when the option above is on.",
+ "background_poll_multiplier_desc": "2-10. Applied to App shell intervals when the option above is on.",
"reduce_interfaces_discovery": "Slow Interfaces page polls",
"reduce_interfaces_discovery_desc": "Uses the intervals below for stats and discovered-interface refresh.",
"interfaces_stats_poll_seconds": "Interfaces stats poll (seconds)",
@@ -2239,7 +2239,7 @@
"description": "Välitä LXMF osoitteiden välillä säilyttäen vastausosoitteen.",
"add_rule": "Lisää välityssääntö",
"name": "Nimi",
- "name_placeholder": "Säännön nimi (esim. kotisolmu)...",
+ "name_placeholder": "Säännön nimi (esim. Kotisolmu)...",
"forward_to_hash": "Välitä kohteeseen",
"destination_placeholder": "Kohteen LXMF-tiiviste...",
"source_filter": "Lähteen suodatin (valinnainen)",
@@ -3042,7 +3042,7 @@
"new_message_toast": "Uusi viesti #{room}",
"host_users": "käyttäjää",
"host_announce_interval": "Ilmoitusväli (minuuttia)",
- "host_announce_interval_hint": "Joka {minutes}. minuutti",
+ "host_announce_interval_hint": "Joka {minutes}. Minuutti",
"host_hub_settings": "Hubin asetukset",
"presence_joined": "{count} liittyi",
"presence_left": "{count} lähti",
@@ -3267,7 +3267,7 @@
"argos_translate": "Argos Translate",
"libretranslate": "LibreTranslate",
"api_server": "LibreTranslate API-palvelin",
- "api_server_description": "Syötä LibreTranslate-palvelimesi perus-URL (esim. https://libretranslate.com tai http://localhost:5000).",
+ "api_server_description": "Syötä LibreTranslate-palvelimesi perus-URL (esim. Https://libretranslate.com tai http://localhost:5000).",
"api_key_optional": "LibreTranslate API-avain (valinnainen)",
"api_key_placeholder": "Jätä tyhjäksi, ellei palveluntarjoajasi vaadi sitä",
"api_key_description": "Tarvittaessa LibreTranslate odottaa tätä api_key-parametrina JSON-käännösrungossa ja valinnaisesti kielipyynnössä. Jätä pois tyypillisissä itse isännöidyissä instansseissa.",
@@ -3357,7 +3357,7 @@
"preview_greeting": "Esikatsele tervehdystä",
"stop_preview": "Lopeta esikatselu",
"answer_delay_seconds": "Vastausviive (s)",
- "max_recording_seconds": "Max. tallennus (s)",
+ "max_recording_seconds": "Max. Tallennus (s)",
"recording_voicemail": "Tallennetaan vastaajaviestiä",
"call_status": "Puhelun tila",
"call_recording": "Puhelun tallennus",
@@ -3487,7 +3487,7 @@
"contact_name_optional": "Yhteystiedon nimi (valinnainen)",
"contact_name_placeholder": "esim. Field Node",
"hash_or_uri": "LXMF-tiiviste tai URI",
- "hash_or_uri_placeholder": "32-merkin tiiviste, lxmf://... tai lxma://...",
+ "hash_or_uri_placeholder": "32-merkin tiiviste, lxmf://... Tai lxma://...",
"paste": "Liitä",
"failed_load_contacts": "Yhteystietojen lataus epäonnistui",
"invalid_contact_input": "Virheellinen syöte. Käytä 32-merkin tiivistettä tai lxma://-URI:a.",
diff --git a/meshchatx/src/frontend/locales/fr.json b/meshchatx/src/frontend/locales/fr.json
index 40df7348..335f86d8 100644
--- a/meshchatx/src/frontend/locales/fr.json
+++ b/meshchatx/src/frontend/locales/fr.json
@@ -221,7 +221,7 @@
"run_local_node": "Lancer un nœud de propagation local",
"run_local_node_description": "MeshChat annoncera et maintiendra un nœud en utilisant ce hachage de destination local.",
"preferred_propagation_node": "Nœud de propagation préféré",
- "preferred_node_placeholder": "Destination hash, p.ex. a39610c89d18bb48c73e429582423c24",
+ "preferred_node_placeholder": "Destination hash, p.ex. A39610c89d18bb48c73e429582423c24",
"fallback_node_description": "Les messages reviennent à ce nœud lorsque la livraison directe échoue.",
"auto_select_node": "Sélection automatique du meilleur noeud",
"auto_select_node_description": "Trouver et passer automatiquement au meilleur nœud de propagation disponible basé sur les sauts réseau.",
@@ -433,8 +433,8 @@
"map_tiles_heading": "Tuiles et hors ligne",
"map_overlay_limits_heading": "Limites des calques distants",
"map_overlay_limits_desc": "Gardes-fous pour les imports KMZ/KML/GeoJSON depuis NomadNet et RNGit. Les valeurs sont limitées à des plages sûres.",
- "map_overlay_max_bytes": "Taille max. du fichier de calque (octets)",
- "map_overlay_max_features": "Entités max. par calque",
+ "map_overlay_max_bytes": "Taille max. Du fichier de calque (octets)",
+ "map_overlay_max_features": "Entités max. Par calque",
"map_overlay_max_kmz_uncompressed_bytes": "Taille max. KMZ non compressé (octets)",
"map_overlay_max_sources": "Sources de calques max.",
"map_overlay_max_concurrent_jobs": "Tâches de calque simultanées max.",
@@ -486,7 +486,7 @@
"copy_rpc_config": "Copier la configuration RPC",
"connected_to_shared_instance": "Ce processus MeshChatX est attaché à une instance Reticulum partagée externe. Modifiez la configuration de cette instance pour changer les paramètres de partage/RPC.",
"copy_failed": "Échec de la copie dans le presse-papiers",
- "rpc_key_hidden": "Masquée — appuyer pour afficher",
+ "rpc_key_hidden": "Masquée. Appuyer pour afficher",
"rpc_key_show": "Afficher la clé RPC",
"rpc_key_hide": "Masquer la clé RPC",
"refresh_community_interfaces": "Actualiser depuis directory.rns.recipes",
@@ -1205,7 +1205,7 @@
"memory_pressure_relaxed": "Active (low host RAM)",
"memory_pressure_paths": "{count} chemins suivis",
"env_host_battery": "Batterie hote",
- "app_battery_use": "Batt. est. MeshChatX",
+ "app_battery_use": "Batt. Est. MeshChatX",
"app_battery_use_hint": "Estimation a partir du temps CPU MeshChatX depuis le demarrage. Pas une attribution OS.",
"app_battery_use_warming": "Mesure…",
"app_battery_use_rate": "~{rate}",
@@ -1415,8 +1415,8 @@
"tile_connectivity_retry": "Réessayer les tuiles",
"tile_connectivity_use_offline": "Mode hors ligne",
"tile_connectivity_dismiss": "Fermer",
- "tile_failover_trying": "Échec des tuiles carte ; essai de {provider}…",
- "tile_failover_offline": "Tuiles en ligne indisponibles ; passage en mode hors ligne",
+ "tile_failover_trying": "Échec des tuiles carte. Essai de {provider}…",
+ "tile_failover_offline": "Tuiles en ligne indisponibles. Passage en mode hors ligne",
"search_no_results": "Aucun résultat trouvé",
"custom_tile_server_unavailable": "Le serveur de tuiles personnalisé n'est pas accessible en mode hors ligne",
"custom_nominatim_unavailable": "Le serveur Nominatim personnalisé n'est pas accessible en mode hors ligne",
@@ -1593,7 +1593,7 @@
"snr": "SNR{snr}",
"stamp_cost": "Coût du timbre{cost}",
"stamp_ticket_valid": "Ticket de timbre sortant actif (envoi instantané). Expire {expires}.",
- "stamp_ticket_expired": "Ticket de timbre expiré ; le prochain message peut nécessiter une preuve de travail.",
+ "stamp_ticket_expired": "Ticket de timbre expiré. Le prochain message peut nécessiter une preuve de travail.",
"pop_out_chat": "Clavardage",
"open_in_split": "Ouvrir en vue partagée",
"select_conversation_for_pane": "Sélectionnez une conversation pour l'ouvrir ici",
@@ -1704,7 +1704,7 @@
"folders_imported": "Dossiers importés",
"failed_import_folders": "Impossible d'importer des dossiers",
"failed_read_clipboard": "Impossible de lire depuis le presse-papiers",
- "clipboard_read_requires_secure_context": "La lecture du presse-papiers nécessite une page sécurisée (https ou http://127.0.0.1). Avec http://0.0.0.0 l’API Clipboard est bloquée—utilisez 127.0.0.1 ou https, ou collez avec Ctrl+V.",
+ "clipboard_read_requires_secure_context": "La lecture du presse-papiers nécessite une page sécurisée (https ou http://127.0.0.1). Avec http://0.0.0.0 l’API Clipboard est bloquée. Utilisez 127.0.0.1 ou https, ou collez avec Ctrl+V.",
"clipboard_write_unavailable": "Copie automatique impossible. Utilisez https ou http://127.0.0.1, ou copiez la sélection manuellement.",
"oversized_body_notice": "Ce message est trop volumineux pour s'afficher ici ({count} caractères). Copiez le texte intégral pour le lire ou le modifier ailleurs.",
"oversized_body_copy": "Copier le texte intégral",
@@ -1766,7 +1766,7 @@
"failed_start_recording": "a échoué à démarrer l'enregistrement",
"failed_start_recording_help_mediadevices": "Cette vue web n'expose pas l'API microphone.",
"failed_start_recording_help_web_audio": "Web Audio n'est pas disponible dans ce navigateur.",
- "failed_start_recording_help_audio_worklet": "AudioWorklet n'est pas pris en charge — les messages vocaux OPUS en ont besoin.",
+ "failed_start_recording_help_audio_worklet": "AudioWorklet n'est pas pris en charge. Les messages vocaux OPUS en ont besoin.",
"failed_start_recording_help_permission": "Autorisez l'accès au microphone, ou réessayez après qu'une autre application libère le microphone.",
"remove_audio_confirm": "Voulez-vous vraiment supprimer cette pièce jointe audio ?",
"failed_generate_qr": "Impossible de générer QR",
@@ -1900,14 +1900,14 @@
"micron_wasm_update_source_upload": "Fichier local (non vérifié)",
"micron_wasm_update_active_size": "Taille : {bytes} octets",
"micron_wasm_update_bundled_only": "Utilisation du WASM fourni avec l’application. Aucun téléchargement réseau tant que vous n’utilisez pas les actions ci-dessous.",
- "micron_wasm_update_isolation_note": "Le navigateur ne peut pas isoler totalement WebAssembly de JavaScript. Cette application n’expose la conversion Micron que via une API restreinte ; le HTML produit reste assaini. wasm_exec.js provient toujours du bundle pour correspondre au runtime Go.",
+ "micron_wasm_update_isolation_note": "Le navigateur ne peut pas isoler totalement WebAssembly de JavaScript. Cette application n’expose la conversion Micron que via une API restreinte. Le HTML produit reste assaini. Wasm_exec.js provient toujours du bundle pour correspondre au runtime Go.",
"micron_wasm_update_github_heading": "Installer depuis une version GitHub",
"micron_wasm_update_github_url_hint": "Appuyez sur Récupérer pour que le serveur MeshChat télécharge depuis GitHub (navigateur same-origin pour la CSP). Chemin source :",
"micron_wasm_update_fetch_github": "Récupérer et installer",
"micron_wasm_update_upload_heading": "Installer depuis un fichier",
"micron_wasm_update_upload_warning": "Le WASM téléversé n’est pas contrôlé pour l’authenticité. N’utilisez que des fichiers de confiance.",
"micron_wasm_update_revert_bundled": "Supprimer le remplacement (WASM fourni)",
- "micron_wasm_update_err_empty_tag": "Saisissez une étiquette de version (par ex. v1.0.5).",
+ "micron_wasm_update_err_empty_tag": "Saisissez une étiquette de version (par ex. V1.0.5).",
"micron_wasm_update_err_not_wasm": "Choisissez un fichier dont le nom se termine par .wasm.",
"micron_wasm_update_err_too_large": "Ce fichier est trop volumineux pour un remplacement d’exécution.",
"micron_wasm_update_err_too_small": "Ce fichier est trop petit pour être un module WASM valide.",
@@ -1954,7 +1954,7 @@
"reduce_background_polling": "Slow background status polling",
"reduce_background_polling_desc": "Multiplies App shell poll intervals (calls, unread, app info).",
"background_poll_multiplier": "Background poll multiplier",
- "background_poll_multiplier_desc": "2–10. Applied to App shell intervals when the option above is on.",
+ "background_poll_multiplier_desc": "2-10. Applied to App shell intervals when the option above is on.",
"reduce_interfaces_discovery": "Slow Interfaces page polls",
"reduce_interfaces_discovery_desc": "Uses the intervals below for stats and discovered-interface refresh.",
"interfaces_stats_poll_seconds": "Interfaces stats poll (seconds)",
diff --git a/meshchatx/src/frontend/locales/it.json b/meshchatx/src/frontend/locales/it.json
index 9910fa16..9d2af588 100644
--- a/meshchatx/src/frontend/locales/it.json
+++ b/meshchatx/src/frontend/locales/it.json
@@ -221,7 +221,7 @@
"run_local_node": "Esegui un nodo di propagazione locale",
"run_local_node_description": "MeshChat annuncerà e manterrà un nodo usando questo hash di destinazione locale.",
"preferred_propagation_node": "Nodo di Propagazione Preferito",
- "preferred_node_placeholder": "Hash di destinazione, es. a39610c89d18bb48c73e429582423c24",
+ "preferred_node_placeholder": "Hash di destinazione, es. A39610c89d18bb48c73e429582423c24",
"fallback_node_description": "I messaggi ripiegano su questo nodo ogni volta che la consegna diretta fallisce.",
"auto_select_node": "Selezione automatica del miglior nodo",
"auto_select_node_description": "Trova e passa automaticamente al miglior nodo di propagazione disponibile in base agli hop di rete.",
@@ -395,7 +395,7 @@
"csp_settings": "Content Security Policy (CSP)",
"csp_description": "Aggiungi manualmente domini consentiti per varie risorse. Utile per server di mappe personalizzati, bot o integrazioni esterne.",
"csp_extra_connect_src": "Sorgenti di Connessione Extra",
- "csp_extra_connect_src_description": "Sorgenti aggiuntive per connessioni fetch/websocket (es. https://api.example.com).",
+ "csp_extra_connect_src_description": "Sorgenti aggiuntive per connessioni fetch/websocket (es. Https://api.example.com).",
"csp_extra_img_src": "Sorgenti Immagine Extra",
"csp_extra_img_src_description": "Sorgenti aggiuntive per immagini e tile.",
"csp_extra_frame_src": "Sorgenti Frame Extra",
@@ -486,7 +486,7 @@
"copy_rpc_config": "Copia configurazione RPC",
"connected_to_shared_instance": "Questo processo MeshChatX è collegato a un'istanza Reticulum condivisa esterna. Modifica la configurazione di quell'istanza per cambiare le impostazioni di condivisione/RPC.",
"copy_failed": "Copia negli appunti fallita",
- "rpc_key_hidden": "Nascosta — tocca per mostrare",
+ "rpc_key_hidden": "Nascosta. Tocca per mostrare",
"rpc_key_show": "Mostra chiave RPC",
"rpc_key_hide": "Nascondi chiave RPC",
"refresh_community_interfaces": "Aggiorna da directory.rns.recipes",
@@ -599,7 +599,7 @@
"file_too_large_video": "Gli sticker video WEBM devono essere di 256 KB o meno.",
"invalid_dimensions": "Gli sticker devono essere al massimo 512×512 pixel con un lato esattamente 512.",
"invalid_duration": "Sticker animati e video: durata massima 3 secondi.",
- "invalid_fps": "Sticker animati: 30–60 FPS. Video: fino a 30 FPS.",
+ "invalid_fps": "Sticker animati: 30-60 FPS. Video: fino a 30 FPS.",
"invalid_codec": "Gli sticker video devono usare il codec VP9.",
"invalid_audio": "Gli sticker video non devono contenere audio.",
"invalid_tgs": "Impossibile leggere l'animazione TGS.",
@@ -613,7 +613,7 @@
"title_label": "Titolo",
"title_placeholder": "Il mio pacchetto",
"short_name_label": "Nome breve",
- "short_name_placeholder": "es. gatti",
+ "short_name_placeholder": "es. Gatti",
"description_label": "Descrizione",
"type_label": "Tipo",
"type_static": "Statico",
@@ -1047,7 +1047,7 @@
"contact_name_optional": "Nome contatto (opzionale)",
"contact_name_placeholder": "es. Nodo di campo",
"hash_or_uri": "Hash LXMF o URI",
- "hash_or_uri_placeholder": "Hash 32 caratteri, lxmf://... o lxma://...",
+ "hash_or_uri_placeholder": "Hash 32 caratteri, lxmf://... O lxma://...",
"paste": "Incolla",
"failed_load_contacts": "Impossibile caricare i contatti",
"invalid_contact_input": "Input non valido. Usa un hash di 32 caratteri o URI lxma://.",
@@ -1257,7 +1257,7 @@
"memory_pressure_relaxed": "Active (low host RAM)",
"memory_pressure_paths": "{count} percorsi tracciati",
"env_host_battery": "Batteria host",
- "app_battery_use": "Batt. stim. MeshChatX",
+ "app_battery_use": "Batt. Stim. MeshChatX",
"app_battery_use_hint": "Stima dal tempo CPU di MeshChatX dall'avvio. Non e attribuzione del SO.",
"app_battery_use_warming": "Misurazione…",
"app_battery_use_rate": "~{rate}",
@@ -1467,8 +1467,8 @@
"tile_connectivity_retry": "Riprova tessere",
"tile_connectivity_use_offline": "Modalità offline",
"tile_connectivity_dismiss": "Chiudi",
- "tile_failover_trying": "Caricamento tessere mappa fallito; provo {provider}…",
- "tile_failover_offline": "Tessere online non disponibili; passaggio alla modalità offline",
+ "tile_failover_trying": "Caricamento tessere mappa fallito. Provo {provider}…",
+ "tile_failover_offline": "Tessere online non disponibili. Passaggio alla modalità offline",
"search_no_results": "Nessun risultato trovato",
"custom_tile_server_unavailable": "Il server tile personalizzato non è accessibile in modalità offline",
"custom_nominatim_unavailable": "Il server Nominatim personalizzato non è accessibile in modalità offline",
@@ -1645,7 +1645,7 @@
"snr": "SNR {snr}",
"stamp_cost": "Costo Francobollo {cost}",
"stamp_ticket_valid": "Biglietto di francobollo in uscita attivo (invio istantaneo). Scade {expires}.",
- "stamp_ticket_expired": "Biglietto scaduto; il prossimo messaggio potrebbe richiedere proof-of-work.",
+ "stamp_ticket_expired": "Biglietto scaduto. Il prossimo messaggio potrebbe richiedere proof-of-work.",
"pop_out_chat": "Estrai chat",
"open_in_split": "Apri in vista divisa",
"select_conversation_for_pane": "Seleziona una conversazione per aprirla qui",
@@ -1760,7 +1760,7 @@
"folders_imported": "Cartelle importate",
"failed_import_folders": "Impossibile importare le cartelle",
"failed_read_clipboard": "Impossibile leggere dagli appunti",
- "clipboard_read_requires_secure_context": "La lettura degli appunti richiede una pagina sicura (https o http://127.0.0.1). Con http://0.0.0.0 l’API Clipboard è bloccata—usa 127.0.0.1 o https, oppure Incolla con Ctrl+V.",
+ "clipboard_read_requires_secure_context": "La lettura degli appunti richiede una pagina sicura (https o http://127.0.0.1). Con http://0.0.0.0 l’API Clipboard è bloccata. Usa 127.0.0.1 o https, oppure Incolla con Ctrl+V.",
"clipboard_write_unavailable": "Impossibile copiare automaticamente. Usa https o http://127.0.0.1, oppure copia manualmente la selezione.",
"oversized_body_notice": "Questo messaggio è troppo grande per essere mostrato qui ({count} caratteri). Copia il testo completo per leggerlo o modificarlo altrove.",
"oversized_body_copy": "Copia testo completo",
@@ -1822,7 +1822,7 @@
"failed_start_recording": "impossibile avviare la registrazione",
"failed_start_recording_help_mediadevices": "Questa webview non espone l'API del microfono.",
"failed_start_recording_help_web_audio": "Web Audio non è disponibile in questo browser.",
- "failed_start_recording_help_audio_worklet": "AudioWorklet non è supportato — le note vocali OPUS lo richiedono.",
+ "failed_start_recording_help_audio_worklet": "AudioWorklet non è supportato. Le note vocali OPUS lo richiedono.",
"failed_start_recording_help_permission": "Consenti l'accesso al microfono o riprova dopo che un'altra app rilascia il microfono.",
"remove_audio_confirm": "Sei sicuro di voler rimuovere questo allegato audio?",
"failed_generate_qr": "Impossibile generare il QR",
@@ -1952,14 +1952,14 @@
"micron_wasm_update_source_upload": "File locale (non verificato)",
"micron_wasm_update_active_size": "Dimensione: {bytes} byte",
"micron_wasm_update_bundled_only": "Si usa il WASM incluso nell’app. Nulla viene scaricato dalla rete finché non usi le azioni sotto.",
- "micron_wasm_update_isolation_note": "Il browser non può isolare completamente WebAssembly da JavaScript. Questa app espone la conversione Micron solo tramite un’API ristretta; l’HTML generato resta sanificato. wasm_exec.js proviene sempre dal bundle dell’app per allinearsi al runtime Go.",
+ "micron_wasm_update_isolation_note": "Il browser non può isolare completamente WebAssembly da JavaScript. Questa app espone la conversione Micron solo tramite un’API ristretta. L’HTML generato resta sanificato. Wasm_exec.js proviene sempre dal bundle dell’app per allinearsi al runtime Go.",
"micron_wasm_update_github_heading": "Installa da release GitHub",
"micron_wasm_update_github_url_hint": "Premi Scarica: il server MeshChat scarica da GitHub (il browser resta same-origin per la CSP). Percorso sorgente:",
"micron_wasm_update_fetch_github": "Scarica e installa",
"micron_wasm_update_upload_heading": "Installa da file",
"micron_wasm_update_upload_warning": "Il WASM caricato non è controllato per autenticità. Usa solo file di cui ti fidi.",
"micron_wasm_update_revert_bundled": "Rimuovi sostituzione (usa WASM incluso)",
- "micron_wasm_update_err_empty_tag": "Inserisci un tag di release (ad es. v1.0.5).",
+ "micron_wasm_update_err_empty_tag": "Inserisci un tag di release (ad es. V1.0.5).",
"micron_wasm_update_err_not_wasm": "Scegli un file il cui nome termina con .wasm.",
"micron_wasm_update_err_too_large": "Il file è troppo grande per essere salvato come sostituzione a runtime.",
"micron_wasm_update_err_too_small": "Il file è troppo piccolo per essere un modulo WASM valido.",
@@ -2006,7 +2006,7 @@
"reduce_background_polling": "Slow background status polling",
"reduce_background_polling_desc": "Multiplies App shell poll intervals (calls, unread, app info).",
"background_poll_multiplier": "Background poll multiplier",
- "background_poll_multiplier_desc": "2–10. Applied to App shell intervals when the option above is on.",
+ "background_poll_multiplier_desc": "2-10. Applied to App shell intervals when the option above is on.",
"reduce_interfaces_discovery": "Slow Interfaces page polls",
"reduce_interfaces_discovery_desc": "Uses the intervals below for stats and discovered-interface refresh.",
"interfaces_stats_poll_seconds": "Interfaces stats poll (seconds)",
@@ -2388,7 +2388,7 @@
"licenses": {
"section_label": "Note legali",
"title": "Licenze di terze parti",
- "description": "Versioni, autori e licenze SPDX o dichiarate delle dipendenze a runtime. I dati provengono dall'ambiente Python installato. Per il frontend si usa pnpm se funziona. Altrimenti i pacchetti si leggono da node_modules (ad es. se pnpm licenses fallisce sul lockfile).",
+ "description": "Versioni, autori e licenze SPDX o dichiarate delle dipendenze a runtime. I dati provengono dall'ambiente Python installato. Per il frontend si usa pnpm se funziona. Altrimenti i pacchetti si leggono da node_modules (ad es. Se pnpm licenses fallisce sul lockfile).",
"short_description": "Versioni, autori e licenze per backend e frontend.",
"search_placeholder": "Cerca pacchetti, versioni, autori, licenze...",
"generated_at": "Generato alle {time}",
@@ -3114,7 +3114,7 @@
"argos_translate": "Argos Translate",
"libretranslate": "LibreTranslate",
"api_server": "Server API LibreTranslate",
- "api_server_description": "Inserisci l'URL di base del tuo server LibreTranslate (es. http://localhost:5000)",
+ "api_server_description": "Inserisci l'URL di base del tuo server LibreTranslate (es. Http://localhost:5000)",
"api_key_optional": "Chiave API LibreTranslate (opzionale)",
"api_key_placeholder": "Lasciare vuoto se il provider non la richiede",
"api_key_description": "Se richiesta, LibreTranslate si aspetta api_key nel corpo JSON translate e facoltativamente nella richiesta lingue. Omettere per installazioni self-hosted tipiche.",
diff --git a/meshchatx/src/frontend/locales/nl.json b/meshchatx/src/frontend/locales/nl.json
index 0f14e72d..e95660a6 100644
--- a/meshchatx/src/frontend/locales/nl.json
+++ b/meshchatx/src/frontend/locales/nl.json
@@ -395,7 +395,7 @@
"csp_settings": "Inhoudsveiligheidsbeleid (CSP)",
"csp_description": "Handmatig toegestane domeinen en bronnen toevoegen voor verschillende soorten bronnen. Dit is nuttig voor aangepaste tile servers, bots, of externe integraties.",
"csp_extra_connect_src": "Extra verbindingsbronnen",
- "csp_extra_connect_src_description": "Aanvullende bronnen voor fetch/websocket-verbindingen (bv. https://api.example.com).",
+ "csp_extra_connect_src_description": "Aanvullende bronnen voor fetch/websocket-verbindingen (bv. Https://api.example.com).",
"csp_extra_img_src": "Extra afbeeldingsbronnen",
"csp_extra_img_src_description": "Aanvullende bronnen voor afbeeldingen en tegels.",
"csp_extra_frame_src": "Extra framebronnen",
@@ -433,15 +433,15 @@
"map_tiles_heading": "Tegels en offline",
"map_overlay_limits_heading": "Limieten voor externe overlays",
"map_overlay_limits_desc": "Beschermingslimieten voor KMZ/KML/GeoJSON-imports via NomadNet en RNGit. Waarden worden begrensd tot veilige bereiken.",
- "map_overlay_max_bytes": "Max. overlay-bestandsgrootte (bytes)",
- "map_overlay_max_features": "Max. objecten per overlay",
- "map_overlay_max_kmz_uncompressed_bytes": "Max. ongecomprimeerde KMZ-grootte (bytes)",
- "map_overlay_max_sources": "Max. overlaybronnen",
- "map_overlay_max_concurrent_jobs": "Max. gelijktijdige overlaytaken",
+ "map_overlay_max_bytes": "Max. Overlay-bestandsgrootte (bytes)",
+ "map_overlay_max_features": "Max. Objecten per overlay",
+ "map_overlay_max_kmz_uncompressed_bytes": "Max. Ongecomprimeerde KMZ-grootte (bytes)",
+ "map_overlay_max_sources": "Max. Overlaybronnen",
+ "map_overlay_max_concurrent_jobs": "Max. Gelijktijdige overlaytaken",
"map_overlay_path_timeout_seconds": "Time-out padzoeken (seconden)",
"map_overlay_transfer_timeout_seconds": "Time-out overdracht (seconden)",
"map_overlay_job_timeout_seconds": "Time-out taak (seconden)",
- "map_overlay_max_retries": "Max. ophaalpogingen",
+ "map_overlay_max_retries": "Max. Ophaalpogingen",
"map_overlay_retry_delay_seconds": "Basisvertraging bij opnieuw proberen (seconden)",
"map_default_lat": "Standaardbreedtegraad",
"map_default_lon": "Standaardlengtegraad",
@@ -486,7 +486,7 @@
"copy_rpc_config": "Kopieer RPC-configuratie",
"connected_to_shared_instance": "Dit MeshChatX-proces is gekoppeld aan een externe gedeelde Reticulum-instantie. Bewerk de configuratie van die instantie om share/RPC-instellingen te wijzigen.",
"copy_failed": "Kopiëren naar klembord mislukt",
- "rpc_key_hidden": "Verborgen — tik om te tonen",
+ "rpc_key_hidden": "Verborgen. Tik om te tonen",
"rpc_key_show": "RPC-sleutel tonen",
"rpc_key_hide": "RPC-sleutel verbergen",
"refresh_community_interfaces": "Vernieuwen vanaf directory.rns.recipes",
@@ -613,7 +613,7 @@
"title_label": "Titel",
"title_placeholder": "Mijn rugzak.",
"short_name_label": "Korte naam",
- "short_name_placeholder": "bv. katten",
+ "short_name_placeholder": "bv. Katten",
"description_label": "Omschrijving",
"type_label": "Type",
"type_static": "Statisch",
@@ -998,7 +998,7 @@
"export_all_failed": "Exporteren van identiteiten is mislukt",
"generate_fresh": "Genereer een nieuwe Reticulum identiteit.",
"display_name": "Naam tonen",
- "display_name_hint": "b.v. geheim agent",
+ "display_name_hint": "b.v. Geheim agent",
"current": "Lopend",
"switch": "Overschakelen naar deze identiteit",
"delete": "Identiteit verwijderen",
@@ -1415,8 +1415,8 @@
"tile_connectivity_retry": "Tegels opnieuw proberen",
"tile_connectivity_use_offline": "Offlinemodus",
"tile_connectivity_dismiss": "Sluiten",
- "tile_failover_trying": "Kaarttegels mislukt; probeer {provider}…",
- "tile_failover_offline": "Online tegels niet beschikbaar; overschakelen naar offline modus",
+ "tile_failover_trying": "Kaarttegels mislukt. Probeer {provider}…",
+ "tile_failover_offline": "Online tegels niet beschikbaar. Overschakelen naar offline modus",
"search_no_results": "Geen resultaten gevonden",
"custom_tile_server_unavailable": "Aangepaste tile-server is niet toegankelijk in offline modus",
"custom_nominatim_unavailable": "Aangepaste Nominatim-server is niet toegankelijk in offline modus",
@@ -1593,7 +1593,7 @@
"snr": "SNR{snr}",
"stamp_cost": "Postzegelkosten{cost}",
"stamp_ticket_valid": "Uitgaand postzegelticket actief (direct verzenden). Verloopt {expires}.",
- "stamp_ticket_expired": "Postzegelticket verlopen; het volgende bericht kan proof-of-work vereisen.",
+ "stamp_ticket_expired": "Postzegelticket verlopen. Het volgende bericht kan proof-of-work vereisen.",
"pop_out_chat": "Chat uitklappen",
"open_in_split": "Openen in gesplitste weergave",
"select_conversation_for_pane": "Selecteer een gesprek om het hier te openen",
@@ -1704,7 +1704,7 @@
"folders_imported": "Mappen geïmporteerd",
"failed_import_folders": "Kon mappen niet importeren",
"failed_read_clipboard": "Lezen van klembord is mislukt",
- "clipboard_read_requires_secure_context": "Klembord lezen vereist een beveiligde pagina (https of http://127.0.0.1). Met http://0.0.0.0 blokkeert de browser de Clipboard-API—gebruik 127.0.0.1 of https, of plak met Ctrl+V.",
+ "clipboard_read_requires_secure_context": "Klembord lezen vereist een beveiligde pagina (https of http://127.0.0.1). Met http://0.0.0.0 blokkeert de browser de Clipboard-API. Gebruik 127.0.0.1 of https, of plak met Ctrl+V.",
"clipboard_write_unavailable": "Automatisch kopiëren mislukt. Gebruik https of http://127.0.0.1, of kopieer handmatig.",
"oversized_body_notice": "Dit bericht is te groot om hier te tonen ({count} tekens). Kopieer de volledige tekst om die elders te lezen of te bewerken.",
"oversized_body_copy": "Volledige tekst kopiëren",
@@ -1766,7 +1766,7 @@
"failed_start_recording": "kon opname niet starten",
"failed_start_recording_help_mediadevices": "Deze webweergave biedt geen microfoon-API.",
"failed_start_recording_help_web_audio": "Web Audio is niet beschikbaar in deze browser.",
- "failed_start_recording_help_audio_worklet": "AudioWorklet wordt niet ondersteund — OPUS-spraaknotities hebben dit nodig.",
+ "failed_start_recording_help_audio_worklet": "AudioWorklet wordt niet ondersteund. OPUS-spraaknotities hebben dit nodig.",
"failed_start_recording_help_permission": "Sta microfoontoegang toe of probeer opnieuw nadat een andere app de microfoon vrijgeeft.",
"remove_audio_confirm": "Weet u zeker dat u deze audiobijlage wilt verwijderen?",
"failed_generate_qr": "Kon QR niet aanmaken",
@@ -1900,14 +1900,14 @@
"micron_wasm_update_source_upload": "Lokaal bestand (niet geverifieerd)",
"micron_wasm_update_active_size": "Grootte: {bytes} bytes",
"micron_wasm_update_bundled_only": "De met de app gebundelde WASM wordt gebruikt. Er wordt niets van het netwerk gehaald tot u onderstaande acties gebruikt.",
- "micron_wasm_update_isolation_note": "De browser kan WebAssembly niet volledig van JavaScript isoleren. Deze app biedt Micron-conversie alleen via een beperkte API; gerenderde HTML wordt nog steeds geschoond. wasm_exec.js komt altijd uit het app-bundel om bij de Go-runtime te passen.",
+ "micron_wasm_update_isolation_note": "De browser kan WebAssembly niet volledig van JavaScript isoleren. Deze app biedt Micron-conversie alleen via een beperkte API. Gerenderde HTML wordt nog steeds geschoond. Wasm_exec.js komt altijd uit het app-bundel om bij de Go-runtime te passen.",
"micron_wasm_update_github_heading": "Installeren vanaf GitHub-release",
"micron_wasm_update_github_url_hint": "Druk op Ophalen zodat de MeshChat-server van GitHub downloadt (browser blijft same-origin voor CSP). Bronpad:",
"micron_wasm_update_fetch_github": "Ophalen en installeren",
"micron_wasm_update_upload_heading": "Installeren vanaf bestand",
"micron_wasm_update_upload_warning": "Geüploade WASM wordt niet op echtheid gecontroleerd. Gebruik alleen bestanden die u vertrouwt.",
"micron_wasm_update_revert_bundled": "Overschrijving verwijderen (gebundelde WASM)",
- "micron_wasm_update_err_empty_tag": "Voer een releasetag in (bijv. v1.0.5).",
+ "micron_wasm_update_err_empty_tag": "Voer een releasetag in (bijv. V1.0.5).",
"micron_wasm_update_err_not_wasm": "Kies een bestand waarvan de naam eindigt op .wasm.",
"micron_wasm_update_err_too_large": "Dit bestand is te groot om als runtime-overschrijving op te slaan.",
"micron_wasm_update_err_too_small": "Dit bestand is te klein om een geldige WASM-module te zijn.",
@@ -1954,7 +1954,7 @@
"reduce_background_polling": "Slow background status polling",
"reduce_background_polling_desc": "Multiplies App shell poll intervals (calls, unread, app info).",
"background_poll_multiplier": "Background poll multiplier",
- "background_poll_multiplier_desc": "2–10. Applied to App shell intervals when the option above is on.",
+ "background_poll_multiplier_desc": "2-10. Applied to App shell intervals when the option above is on.",
"reduce_interfaces_discovery": "Slow Interfaces page polls",
"reduce_interfaces_discovery_desc": "Uses the intervals below for stats and discovered-interface refresh.",
"interfaces_stats_poll_seconds": "Interfaces stats poll (seconds)",
@@ -2044,7 +2044,7 @@
"batch": "Lot",
"loading_status": "Laden status",
"failed_load": "Kon netwerkgegevens niet laden",
- "max_hops_filter": "Max. hop",
+ "max_hops_filter": "Max. Hop",
"show_disabled_interfaces": "Offline interfaces tonen",
"show_discovered_interfaces": "Ontdekte interfaces tonen",
"refresh": "Refresh",
@@ -2336,7 +2336,7 @@
"licenses": {
"section_label": "Juridisch",
"title": "Vergunningen van derden",
- "description": "Runtime afhankelijkheid versies, auteurs, en SPDX of verklaarde licentie strings. Gegevens worden verzameld uit de geïnstalleerde Python omgeving. Frontend data maakt gebruik van pnpm wanneer het werkt. Anders worden pakketten gelezen van node modules (bijv. wanneer pnpm-licenties niet werken op het slotbestand).",
+ "description": "Runtime afhankelijkheid versies, auteurs, en SPDX of verklaarde licentie strings. Gegevens worden verzameld uit de geïnstalleerde Python omgeving. Frontend data maakt gebruik van pnpm wanneer het werkt. Anders worden pakketten gelezen van node modules (bijv. Wanneer pnpm-licenties niet werken op het slotbestand).",
"short_description": "Afhankelijkheid versies, auteurs, en licenties voor backend en frontend.",
"search_placeholder": "Zoekpakketten, versies, auteurs, licenties...",
"generated_at": "Gegenereerd op{time}",
@@ -3062,7 +3062,7 @@
"argos_translate": "Argos vertalen",
"libretranslate": "LibreVertalen",
"api_server": "LibreVertaal API-server",
- "api_server_description": "Voer de basis-URL in van uw LibreTranslate-server (bijv. http://localhost:5000)",
+ "api_server_description": "Voer de basis-URL in van uw LibreTranslate-server (bijv. Http://localhost:5000)",
"api_key_optional": "LibreTranslate API-sleutel (optioneel)",
"api_key_placeholder": "Leeg laten tenzij uw provider er een vereist",
"api_key_description": "Indien vereist verwacht LibreTranslate dit als api_key in de JSON-vertaalbody en optioneel bij de talenrequest. Weglaten voor typische zelfgehoste installaties.",
@@ -3152,7 +3152,7 @@
"preview_greeting": "Voorbeeldgroet",
"stop_preview": "Voorbeeld stoppen",
"answer_delay_seconds": "Antwoord vertraging (en)",
- "max_recording_seconds": "Max. opname (s)",
+ "max_recording_seconds": "Max. Opname (s)",
"recording_voicemail": "Voicemail opnemen",
"call_status": "Oproepstatus",
"call_recording": "Oproeprecorder",
@@ -3280,7 +3280,7 @@
"load_more": "Meer laden",
"add_contact": "Contact toevoegen",
"contact_name_optional": "Contactnaam (facultatief)",
- "contact_name_placeholder": "b.v. veldknooppunt",
+ "contact_name_placeholder": "b.v. Veldknooppunt",
"hash_or_uri": "LXMF Hash of URI",
"hash_or_uri_placeholder": "32-char hash, lxmf://..., of lxma://...",
"paste": "Plakken",
diff --git a/meshchatx/src/frontend/locales/ru.json b/meshchatx/src/frontend/locales/ru.json
index d87ad58b..5052848c 100644
--- a/meshchatx/src/frontend/locales/ru.json
+++ b/meshchatx/src/frontend/locales/ru.json
@@ -336,7 +336,7 @@
"loading_overlay_kicker": "Подождите",
"loading_overlay_title": "Смена профиля",
"loading_overlay_subtitle": "Загрузка чатов и настроек. Обычно это занимает несколько секунд.",
- "emergency_mode_active": "Аварийный режим активен — используется база данных в оперативной памяти и ограниченные службы.",
+ "emergency_mode_active": "Аварийный режим активен. Используется база данных в оперативной памяти и ограниченные службы.",
"backend_disconnected": "Нет соединения с сервером",
"backend_process_stopped": "Сервер Reticulum остановлен",
"backend_reconnected": "Соединение с сервером восстановлено",
@@ -356,9 +356,9 @@
"announce_store_lxst": "LXST телефония (lxst.telephony)",
"announce_store_nomad": "Узлы NomadNet (nomadnetwork.node)",
"announce_store_prop": "Узлы рассылки (lxmf.propagation)",
- "announce_max_stored_heading": "Макс. в базе на аспект",
+ "announce_max_stored_heading": "Макс. В базе на аспект",
"announce_fetch_limit_heading": "Размер страницы по умолчанию (API)",
- "announce_search_max_fetch": "Макс. объявлений для поиска",
+ "announce_search_max_fetch": "Макс. Объявлений для поиска",
"announce_search_max_fetch_hint": "Максимум строк из БД при текстовом поиске (на запрос).",
"discovered_interfaces_max_return": "Лимит обнаруженных интерфейсов",
"discovered_interfaces_max_return_hint": "Максимум записей об обнаруженных и активных интерфейсах в интерфейсе.",
@@ -433,15 +433,15 @@
"map_tiles_heading": "Тайлы и офлайн",
"map_overlay_limits_heading": "Лимиты удалённых оверлеев",
"map_overlay_limits_desc": "Ограничения для импорта KMZ/KML/GeoJSON из NomadNet и RNGit. Значения ограничиваются безопасным диапазоном.",
- "map_overlay_max_bytes": "Макс. размер файла оверлея (байты)",
- "map_overlay_max_features": "Макс. число объектов в оверлее",
- "map_overlay_max_kmz_uncompressed_bytes": "Макс. несжатый размер KMZ (байты)",
- "map_overlay_max_sources": "Макс. число источников оверлеев",
- "map_overlay_max_concurrent_jobs": "Макс. одновременных заданий оверлея",
+ "map_overlay_max_bytes": "Макс. Размер файла оверлея (байты)",
+ "map_overlay_max_features": "Макс. Число объектов в оверлее",
+ "map_overlay_max_kmz_uncompressed_bytes": "Макс. Несжатый размер KMZ (байты)",
+ "map_overlay_max_sources": "Макс. Число источников оверлеев",
+ "map_overlay_max_concurrent_jobs": "Макс. Одновременных заданий оверлея",
"map_overlay_path_timeout_seconds": "Таймаут поиска пути (секунды)",
"map_overlay_transfer_timeout_seconds": "Таймаут передачи (секунды)",
"map_overlay_job_timeout_seconds": "Таймаут задания (секунды)",
- "map_overlay_max_retries": "Макс. число повторов загрузки",
+ "map_overlay_max_retries": "Макс. Число повторов загрузки",
"map_overlay_retry_delay_seconds": "Базовая задержка повтора (секунды)",
"map_default_lat": "Широта по умолчанию",
"map_default_lon": "Долгота по умолчанию",
@@ -486,7 +486,7 @@
"copy_rpc_config": "Копировать конфигурацию RPC",
"connected_to_shared_instance": "Этот процесс MeshChatX подключен к внешнему общему экземпляру Reticulum. Отредактируйте конфигурацию этого экземпляра, чтобы изменить настройки share/RPC.",
"copy_failed": "Не удалось скопировать в буфер обмена",
- "rpc_key_hidden": "Скрыт — нажмите, чтобы показать",
+ "rpc_key_hidden": "Скрыт. Нажмите, чтобы показать",
"rpc_key_show": "Показать RPC-ключ",
"rpc_key_hide": "Скрыть RPC-ключ",
"refresh_community_interfaces": "Обновить из directory.rns.recipes",
@@ -594,12 +594,12 @@
"format_video": "Видео",
"format_legacy": "Устаревший",
"errors": {
- "file_too_large_static": "Статичные стикеры — не больше 512 КБ.",
- "file_too_large_animated": "Анимированные TGS — не больше 64 КБ.",
- "file_too_large_video": "Видео WEBM — не больше 256 КБ.",
+ "file_too_large_static": "Статичные стикеры. Не больше 512 КБ.",
+ "file_too_large_animated": "Анимированные TGS. Не больше 64 КБ.",
+ "file_too_large_video": "Видео WEBM. Не больше 256 КБ.",
"invalid_dimensions": "Стикеры не больше 512×512 пикселей, одна сторона ровно 512.",
- "invalid_duration": "Анимированные и видео — не длиннее 3 секунд.",
- "invalid_fps": "Анимация: 30–60 к/с, видео — до 30 к/с.",
+ "invalid_duration": "Анимированные и видео. Не длиннее 3 секунд.",
+ "invalid_fps": "Анимация: 30-60 к/с, видео. До 30 к/с.",
"invalid_codec": "Видеостикеры должны использовать кодек VP9.",
"invalid_audio": "В видеостикерах не должно быть звуковой дорожки.",
"invalid_tgs": "Не удалось прочитать анимацию TGS.",
@@ -613,7 +613,7 @@
"title_label": "Название",
"title_placeholder": "Мой набор",
"short_name_label": "Краткое имя",
- "short_name_placeholder": "напр. коты",
+ "short_name_placeholder": "напр. Коты",
"description_label": "Описание",
"type_label": "Тип",
"type_static": "Статичные",
@@ -1047,7 +1047,7 @@
"contact_name_optional": "Имя контакта (необязательно)",
"contact_name_placeholder": "например, Полевой узел",
"hash_or_uri": "LXMF-хэш или URI",
- "hash_or_uri_placeholder": "32-символьный хэш, lxmf://... или lxma://...",
+ "hash_or_uri_placeholder": "32-символьный хэш, lxmf://... Или lxma://...",
"paste": "Вставить",
"failed_load_contacts": "Не удалось загрузить контакты",
"invalid_contact_input": "Недопустимый ввод. Используйте 32-символьный хэш или URI lxma://.",
@@ -1257,12 +1257,12 @@
"memory_pressure_relaxed": "Active (low host RAM)",
"memory_pressure_paths": "{count} путей отслеживается",
"env_host_battery": "Батарея хоста",
- "app_battery_use": "Оц. батарея MeshChatX",
+ "app_battery_use": "Оц. Батарея MeshChatX",
"app_battery_use_hint": "Оценка по CPU-времени MeshChatX с запуска. Не системная атрибуция.",
"app_battery_use_warming": "Измерение…",
"app_battery_use_rate": "~{rate}",
"app_battery_use_with_intensity": "~{rate} ({intensity})",
- "app_battery_share": "Оц. доля CPU",
+ "app_battery_share": "Оц. Доля CPU",
"app_battery_share_value": "{percent}% устройства",
"app_battery_intensity_low": "низкая",
"app_battery_intensity_moderate": "средняя",
@@ -1321,16 +1321,16 @@
"discovery_list_refresh_failed": "Не удалось обновить список обнаруженных интерфейсов.",
"discovery_default_bootstrap_only": "По умолчанию только bootstrap для нового исходящего TCP",
"discovery_default_bootstrap_only_hint": "Новые TCP-клиент и backbone-коннектор получают bootstrap_only, чтобы Reticulum отключил их при достижении autoconnect_discovered_interfaces.",
- "discovery_default_bootstrap_docs_link": "См. раздел о bootstrap-подключении Reticulum.",
+ "discovery_default_bootstrap_docs_link": "См. Раздел о bootstrap-подключении Reticulum.",
"backbone_transport_identity_label": "Идентичность транспорта (необязательно)",
"backbone_transport_identity_placeholder": "e53433e51cde34c42a3245ba3fe1ad69",
"backbone_transport_identity_hint": "Как в примерах удалённого backbone Reticulum только с remote и target_port. Указывайте хеш, если это задал оператор или каталог.",
"loopback_local_title": "Локальный / loopback",
"loopback_local_body": "Reticulum использует общую инстанцию внутри. Чтобы связаться с другим процессом RNS на этом хосте, используйте TCP-клиент на 127.0.0.1 с портом инстанции или внешний модуль интерфейса.",
- "loopback_local_docs_hint": "См. главу Interfaces о поддерживаемых типах.",
+ "loopback_local_docs_hint": "См. Главу Interfaces о поддерживаемых типах.",
"loopback_local_docs_link": "Открыть встроенное руководство Reticulum",
"custom_external_intro": "Типы вроде WeaveInterface загружаются, если Reticulum находит подходящий файл модуля (TypeName.py) в interfacepath с interface_class (внешний загрузчик RNS). Поля JSON попадают в секцию interface в вашем конфиге.",
- "custom_external_install_intro": "В Android папка Android/data часто скрыта в приложении «Файлы». Используйте «Установить модуль», чтобы скопировать TypeName.py в interfacepath этого приложения через системный выбор файлов (Загрузки, Диск, Bluetooth и т. д.).",
+ "custom_external_install_intro": "В Android папка Android/data часто скрыта в приложении «Файлы». Используйте «Установить модуль», чтобы скопировать TypeName.py в interfacepath этого приложения через системный выбор файлов (Загрузки, Диск, Bluetooth и т. Д.).",
"custom_external_interfacepath_label": "Активный interfacepath",
"custom_external_install_button": "Установить модуль (.py)",
"custom_external_install_overwrite": "Перезаписать, если есть",
@@ -1342,12 +1342,12 @@
"custom_external_module_delete_failed": "Не удалось удалить модуль интерфейса",
"custom_external_type_label": "Имя типа интерфейса",
"custom_external_type_placeholder": "WeaveInterface",
- "custom_external_json_label": "Доп. параметры (JSON-объект)",
+ "custom_external_json_label": "Доп. Параметры (JSON-объект)",
"custom_external_json_placeholder": "\"listen_ip\": \"0.0.0.0\", \"listen_port\": 4242",
- "custom_external_docs_hint": "См. руководство Interfaces про встроенные типы и внешние модули.",
+ "custom_external_docs_hint": "См. Руководство Interfaces про встроенные типы и внешние модули.",
"custom_external_docs_link": "Открыть встроенное руководство Reticulum",
"custom_external_type_required": "Укажите имя типа интерфейса Reticulum.",
- "custom_external_json_invalid": "Некорректный JSON для доп. параметров.",
+ "custom_external_json_invalid": "Некорректный JSON для доп. Параметров.",
"failed_save_discovery": "Не удалось сохранить настройки обнаружения",
"no_interfaces_found_config": "В выбранном файле конфигурации интерфейсы не найдены",
"failed_parse_config": "Не удалось разобрать файл конфигурации",
@@ -1391,7 +1391,7 @@
"listen_ip_required": "Укажите IP для прослушивания.",
"kernel_iface_picker_title": "Интерфейсы на этом узле",
"kernel_iface_loading": "Загрузка…",
- "kernel_iface_picker_help": "Поле «устройство» необязательно: одно имя интерфейса ядра или пусто — привязка только по Listen IP. Нажмите строку, чтобы подставить имя.",
+ "kernel_iface_picker_help": "Поле «устройство» необязательно: одно имя интерфейса ядра или пусто. Привязка только по Listen IP. Нажмите строку, чтобы подставить имя.",
"auto_iface_ifname_chips_hint": "Интерфейсы этого узла: нажмите, чтобы добавить или убрать имя в поле выше.",
"i2p_requirements_title": "Правила интерфейса I2P",
"i2p_requirements_body": "Допускается только один интерфейс I2P. Сначала включите режим транспорта в настройках, затем добавьте I2P последним. Не импортируйте I2P из файла конфигурации и не редактируйте его в сыром редакторе. Изменения, оставляющие I2P не последним, автоматически исправляются при сохранении и запуске.",
@@ -1438,8 +1438,8 @@
"set_as_default": "Установить как вид по умолчанию",
"export_area": "Экспорт области",
"export_instructions": "Выделите область на карте для экспорта.",
- "min_zoom": "Мин. масштаб",
- "max_zoom": "Макс. масштаб",
+ "min_zoom": "Мин. Масштаб",
+ "max_zoom": "Макс. Масштаб",
"tile_count": "Оценка тайлов",
"start_export": "Начать экспорт",
"exporting": "Экспорт карты...",
@@ -1467,8 +1467,8 @@
"tile_connectivity_retry": "Повторить загрузку тайлов",
"tile_connectivity_use_offline": "Офлайн-режим",
"tile_connectivity_dismiss": "Закрыть",
- "tile_failover_trying": "Не удалось загрузить тайлы карты; пробую {provider}…",
- "tile_failover_offline": "Онлайн-тайлы недоступны; переключение в автономный режим",
+ "tile_failover_trying": "Не удалось загрузить тайлы карты. Пробую {provider}…",
+ "tile_failover_offline": "Онлайн-тайлы недоступны. Переключение в автономный режим",
"search_no_results": "Результаты не найдены",
"custom_tile_server_unavailable": "Пользовательский сервер тайлов недоступен в офлайн-режиме",
"custom_nominatim_unavailable": "Пользовательский сервер Nominatim недоступен в офлайн-режиме",
@@ -1645,7 +1645,7 @@
"snr": "SNR {snr}",
"stamp_cost": "Стоимость штампа {cost}",
"stamp_ticket_valid": "Активный исходящий билет штампа (мгновенная отправка). Истекает {expires}.",
- "stamp_ticket_expired": "Билет штампа истёк; следующее сообщение может потребовать proof-of-work.",
+ "stamp_ticket_expired": "Билет штампа истёк. Следующее сообщение может потребовать proof-of-work.",
"pop_out_chat": "Открыть в отдельном окне",
"open_in_split": "Открыть в разделённом виде",
"select_conversation_for_pane": "Выберите беседу, чтобы открыть её здесь",
@@ -1760,7 +1760,7 @@
"folders_imported": "Папки импортированы",
"failed_import_folders": "Не удалось импортировать папки",
"failed_read_clipboard": "Не удалось прочитать из буфера обмена",
- "clipboard_read_requires_secure_context": "Для чтения буфера обмена требуется безопасная страница (https или http://127.0.0.1). Использование http://0.0.0.0 или другого нелокального HTTP-источника блокирует API буфера обмена — откройте приложение на 127.0.0.1 или https, либо вставьте с помощью Ctrl+V.",
+ "clipboard_read_requires_secure_context": "Для чтения буфера обмена требуется безопасная страница (https или http://127.0.0.1). Использование http://0.0.0.0 или другого нелокального HTTP-источника блокирует API буфера обмена. Откройте приложение на 127.0.0.1 или https, либо вставьте с помощью Ctrl+V.",
"clipboard_write_unavailable": "Не удалось скопировать автоматически. Откройте на https или http://127.0.0.1, либо выделите текст и скопируйте вручную.",
"oversized_body_notice": "Сообщение слишком длинное для отображения здесь ({count} символов). Скопируйте полный текст, чтобы прочитать или изменить его в другом приложении.",
"oversized_body_copy": "Копировать полный текст",
@@ -1819,7 +1819,7 @@
"failed_start_recording": "Не удалось начать запись",
"failed_start_recording_help_mediadevices": "Этот веб-интерфейс не предоставляет доступ к микрофону.",
"failed_start_recording_help_web_audio": "Web Audio недоступен в этом браузере.",
- "failed_start_recording_help_audio_worklet": "AudioWorklet не поддерживается — для голосовых заметок OPUS требуется его поддержка.",
+ "failed_start_recording_help_audio_worklet": "AudioWorklet не поддерживается. Для голосовых заметок OPUS требуется его поддержка.",
"failed_start_recording_help_permission": "Разрешите доступ к микрофону или повторите попытку после того, как другое приложение освободит микрофон.",
"remove_audio_confirm": "Вы уверены, что хотите удалить это аудиовложение?",
"failed_generate_qr": "Не удалось сгенерировать QR-код",
@@ -2420,7 +2420,7 @@
"eeprom_console_hint": "Дампы EEPROM отображаются в консоли инструментов разработчика.",
"configure_bluetooth": "Настройка Bluetooth (опционально)",
"bluetooth_info_1": "Bluetooth поддерживается не на всех устройствах.",
- "bluetooth_info_2": "Некоторые устройства используют Bluetooth Classic, а некоторые — BLE (Bluetooth Low Energy).",
+ "bluetooth_info_2": "Некоторые устройства используют Bluetooth Classic, а некоторые. BLE (Bluetooth Low Energy).",
"bluetooth_info_3": "Переведите RNode в режим сопряжения Bluetooth, затем подключитесь к нему в настройках Bluetooth Android.",
"bluetooth_info_4": "После инициирования запроса на сопряжение из Android, PIN-код должен отобразиться на экране RNode.",
"bluetooth_info_5": "В Sideband вам нужно включить 'Подключиться через Bluetooth' в Оборудование → RNode.",
@@ -2956,7 +2956,7 @@
"preview_greeting": "Предпросмотр приветствия",
"stop_preview": "Остановить предпросмотр",
"answer_delay_seconds": "Задержка ответа (с)",
- "max_recording_seconds": "Макс. время записи (с)",
+ "max_recording_seconds": "Макс. Время записи (с)",
"recording_voicemail": "Запись голосового сообщения",
"call_status": "Статус звонка",
"call_recording": "Запись звонков",
@@ -3344,7 +3344,7 @@
"micron_wasm_update_source_upload": "Локальный файл (без проверки подлинности)",
"micron_wasm_update_active_size": "Размер: {bytes} байт",
"micron_wasm_update_bundled_only": "Используется WASM, поставляемый с приложением. Сеть не запрашивается, пока вы не выполните действия ниже.",
- "micron_wasm_update_isolation_note": "Браузер не может полностью изолировать WebAssembly от JavaScript. Приложение выставляет преобразование Micron только через узкий API; HTML по-прежнему очищается. wasm_exec.js всегда берётся из пакета приложения для соответствия среде Go.",
+ "micron_wasm_update_isolation_note": "Браузер не может полностью изолировать WebAssembly от JavaScript. Приложение выставляет преобразование Micron только через узкий API. HTML по-прежнему очищается. Wasm_exec.js всегда берётся из пакета приложения для соответствия среде Go.",
"micron_wasm_update_github_heading": "Установка из релиза GitHub",
"micron_wasm_update_github_url_hint": "Нажмите «Загрузить», чтобы сервер MeshChat скачал с GitHub (браузер остаётся same-origin для CSP). Путь источника:",
"micron_wasm_update_fetch_github": "Загрузить и установить",
@@ -3398,7 +3398,7 @@
"reduce_background_polling": "Slow background status polling",
"reduce_background_polling_desc": "Multiplies App shell poll intervals (calls, unread, app info).",
"background_poll_multiplier": "Background poll multiplier",
- "background_poll_multiplier_desc": "2–10. Applied to App shell intervals when the option above is on.",
+ "background_poll_multiplier_desc": "2-10. Applied to App shell intervals when the option above is on.",
"reduce_interfaces_discovery": "Slow Interfaces page polls",
"reduce_interfaces_discovery_desc": "Uses the intervals below for stats and discovered-interface refresh.",
"interfaces_stats_poll_seconds": "Interfaces stats poll (seconds)",
@@ -3488,7 +3488,7 @@
"batch": "Пакет",
"loading_status": "Статус загрузки",
"failed_load": "Не удалось загрузить данные сети",
- "max_hops_filter": "Макс. хопов",
+ "max_hops_filter": "Макс. Хопов",
"show_disabled_interfaces": "Показать отключенные интерфейсы",
"show_discovered_interfaces": "Показать обнаруженные интерфейсы",
"refresh": "Refresh",
diff --git a/meshchatx/src/frontend/locales/zh.json b/meshchatx/src/frontend/locales/zh.json
index 86551278..7c951152 100644
--- a/meshchatx/src/frontend/locales/zh.json
+++ b/meshchatx/src/frontend/locales/zh.json
@@ -486,7 +486,7 @@
"copy_rpc_config": "复制RPC配置",
"connected_to_shared_instance": "此MeshChatX进程已附加到外部共享的Reticulum实例。编辑该实例的配置以更改共享/RPC设置。",
"copy_failed": "复制到剪贴板失败",
- "rpc_key_hidden": "已隐藏 — 点按显示",
+ "rpc_key_hidden": "已隐藏,点按显示",
"rpc_key_show": "显示 RPC 密钥",
"rpc_key_hide": "隐藏 RPC 密钥",
"refresh_community_interfaces": "从 directory.rns.recipes 刷新",
@@ -1704,7 +1704,7 @@
"folders_imported": "文件夹已导入",
"failed_import_folders": "导入文件夹失败",
"failed_read_clipboard": "从剪贴板读取失败",
- "clipboard_read_requires_secure_context": "读取剪贴板需要安全页面(https 或 http://127.0.0.1)。使用 http://0.0.0.0 等非本地 HTTP 地址会阻止剪贴板 API—请改用 127.0.0.1 或 https,或使用 Ctrl+V 粘贴。",
+ "clipboard_read_requires_secure_context": "读取剪贴板需要安全页面(https 或 http://127.0.0.1)。使用 http://0.0.0.0 等非本地 HTTP 地址会阻止剪贴板 API。请改用 127.0.0.1 或 https,或使用 Ctrl+V 粘贴。",
"clipboard_write_unavailable": "无法自动复制。请使用 https 或 http://127.0.0.1,或手动选择文本复制。",
"oversized_body_notice": "此消息过长,无法在此显示({count} 个字符)。请复制全文以便在其他应用中阅读或编辑。",
"oversized_body_copy": "复制全文",
@@ -1766,7 +1766,7 @@
"failed_start_recording": "无法开始录制",
"failed_start_recording_help_mediadevices": "此 WebView 未提供麦克风 API。",
"failed_start_recording_help_web_audio": "此浏览器不支持 Web Audio。",
- "failed_start_recording_help_audio_worklet": "不支持 AudioWorklet — OPUS 语音留言需要它。",
+ "failed_start_recording_help_audio_worklet": "不支持 AudioWorklet。OPUS 语音留言需要它。",
"failed_start_recording_help_permission": "请允许麦克风权限,或在其他应用释放麦克风后重试。",
"remove_audio_confirm": "您确定要删除此音频附件吗?",
"failed_generate_qr": "生成二维码失败",
@@ -1954,7 +1954,7 @@
"reduce_background_polling": "Slow background status polling",
"reduce_background_polling_desc": "Multiplies App shell poll intervals (calls, unread, app info).",
"background_poll_multiplier": "Background poll multiplier",
- "background_poll_multiplier_desc": "2–10. Applied to App shell intervals when the option above is on.",
+ "background_poll_multiplier_desc": "2-10. Applied to App shell intervals when the option above is on.",
"reduce_interfaces_discovery": "Slow Interfaces page polls",
"reduce_interfaces_discovery_desc": "Uses the intervals below for stats and discovered-interface refresh.",
"interfaces_stats_poll_seconds": "Interfaces stats poll (seconds)",
@@ -2578,7 +2578,7 @@
"enter_dfu_mode": "输入 DFU 模式",
"entering_dfu_mode": "正在输入 DFU 模式...",
"find_device_issue": "找不到你的装置吗? 打开一个问题",
- "select_firmware": "2. 选择要闪烁的固件 (. zip)",
+ "select_firmware": "2. 选择要闪烁的固件 (. Zip)",
"flash_now": "现在闪光",
"flashing": "闪光:{percentage}%",
"flashing_file_progress": "文件{current}/{total}:{percentage}%",
diff --git a/meshchatx/src/frontend/theme/designTokens.js b/meshchatx/src/frontend/theme/designTokens.js
index b7ff9bfe..f3a5b404 100644
--- a/meshchatx/src/frontend/theme/designTokens.js
+++ b/meshchatx/src/frontend/theme/designTokens.js
@@ -121,7 +121,7 @@ export const MESHCHAT_THEME_VARIABLES_DARK = {
};
/**
- * Tailwind semantic colors (`sem-*` in class names) — `@theme` in style.css mirrors these keys; same --mc-* variables as global CSS.
+ * Tailwind semantic colors (`sem-*` in class names). `@theme` in style.css mirrors these keys. Same --mc-* variables as global CSS.
* Keep keys in sync with usages in style.css and Vue (e.g. bg-sem-canvas).
*/
export function tailwindSemanticColorExtend() {
diff --git a/tests/backend/test_android_codec2.py b/tests/backend/test_android_codec2.py
index 6d7f6c15..e076399f 100644
--- a/tests/backend/test_android_codec2.py
+++ b/tests/backend/test_android_codec2.py
@@ -115,7 +115,7 @@ def test_jni_libs_synced_for_all_abis():
for abi in ("arm64-v8a", "armeabi-v7a", "x86_64"):
lib = jni / abi / "libcodec2.so"
if not lib.is_file():
- pytest.skip(f"missing jniLibs {lib} — not an Android build")
+ pytest.skip(f"missing jniLibs {lib}: not an Android build")
assert lib.stat().st_size > 100_000
diff --git a/tests/backend/test_benchmark_gate.py b/tests/backend/test_benchmark_gate.py
index b22e1c99..c3ad97e1 100644
--- a/tests/backend/test_benchmark_gate.py
+++ b/tests/backend/test_benchmark_gate.py
@@ -60,7 +60,7 @@ class TestBenchmarkStats(unittest.TestCase):
self.assertIn("noise floor", reason)
def test_should_alert_skips_tiny_abs_delta(self):
- # 3x ratio but only +0.4 ms absolute — not actionable
+ # 3x ratio but only +0.4 ms absolute: not actionable
alert, reason = should_alert_regression(0.6, 0.2, noise_floor_ms=0.1)
self.assertFalse(alert)
self.assertIn("abs delta", reason)
@@ -72,7 +72,7 @@ class TestBenchmarkStats(unittest.TestCase):
self.assertIn("slower", reason)
def test_should_alert_within_adaptive_threshold(self):
- # 1.8x on a 10 ms baseline — adaptive threshold is 2.0x
+ # 1.8x on a 10 ms baseline: adaptive threshold is 2.0x
alert, reason = should_alert_regression(18.0, 10.0)
self.assertFalse(alert)
self.assertIn("within adaptive", reason)
diff --git a/tests/backend/test_dao_fuzzing.py b/tests/backend/test_dao_fuzzing.py
index ae4c437a..43cd0998 100644
--- a/tests/backend/test_dao_fuzzing.py
+++ b/tests/backend/test_dao_fuzzing.py
@@ -190,7 +190,7 @@ class TestConfigDAOFuzzing:
# ===================================================================
-# MiscDAO — spam keywords, notifications, keyboard shortcuts
+# MiscDAO: spam keywords, notifications, keyboard shortcuts
# ===================================================================
@@ -490,7 +490,7 @@ class TestMapDrawingsDAOFuzzing:
# ===================================================================
-# MessageDAO — folders
+# MessageDAO: folders
# ===================================================================
@@ -527,7 +527,7 @@ class TestMessageDAOFoldersFuzzing:
# ===================================================================
-# MessageHandler — search
+# MessageHandler: search
# ===================================================================
@@ -597,7 +597,7 @@ class TestMessageHandlerFuzzing:
# ===================================================================
-# _safe_href — URL sanitization
+# _safe_href: URL sanitization
# ===================================================================
diff --git a/tests/backend/test_licenses_collector.py b/tests/backend/test_licenses_collector.py
index 3a7603c7..66969d40 100644
--- a/tests/backend/test_licenses_collector.py
+++ b/tests/backend/test_licenses_collector.py
@@ -26,7 +26,7 @@ def test_flatten_pnpm_licenses_json_maps_and_sorts():
],
"Apache-2.0": [
{"name": "alpha-pkg", "versions": ["1.0.0"], "author": "Alice"},
- {"name": "no-version", "versions": [], "author": "—"},
+ {"name": "no-version", "versions": [], "author": "-"},
],
}
rows = _flatten_pnpm_licenses_json(data)
diff --git a/tests/backend/test_performance_hotpaths.py b/tests/backend/test_performance_hotpaths.py
index ac4341c3..65984cd6 100644
--- a/tests/backend/test_performance_hotpaths.py
+++ b/tests/backend/test_performance_hotpaths.py
@@ -230,11 +230,11 @@ class TestPerformanceHotPaths(unittest.TestCase):
print("--- Seeding complete ---\n")
# ===================================================================
- # ANNOUNCES — load, search, count
+ # ANNOUNCES: load, search, count
# ===================================================================
def test_announce_load_filtered_latency(self):
- """Load announces filtered by aspect with pagination — the NomadNet browser default view."""
+ """Load announces filtered by aspect with pagination: the NomadNet browser default view."""
print("\n[Announce] Filtered load (aspect + pagination):")
durations = []
offsets = [0, 100, 500, 1000, 2000]
@@ -275,7 +275,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
self.assertLess(stats["p95"], 150, "Announce search p95 > 150ms")
def test_announce_search_with_blocked(self):
- """Search with a block-list — simulates real NomadNet browser filtering."""
+ """Search with a block-list: simulates real NomadNet browser filtering."""
print("\n[Announce] Search with blocked list:")
blocked = [secrets.token_hex(16) for _ in range(50)]
durations = []
@@ -312,7 +312,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
# ===================================================================
def test_favourites_load_latency(self):
- """Load all favourites — typically displayed in sidebar."""
+ """Load all favourites: typically displayed in sidebar."""
print("\n[Favourites] Load all:")
durations = []
for _ in range(50):
@@ -340,11 +340,11 @@ class TestPerformanceHotPaths(unittest.TestCase):
self.assertGreater(stats["ops"], 500, "Favourite upsert < 500 ops/s")
# ===================================================================
- # CONVERSATIONS — load, search
+ # CONVERSATIONS: load, search
# ===================================================================
def test_conversations_load_latency(self):
- """Load conversation list — the main messages sidebar query."""
+ """Load conversation list: the main messages sidebar query."""
print("\n[Conversations] Load list (with JOINs):")
durations = []
for _ in range(20):
@@ -360,7 +360,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
self.assertLess(stats["p95"], 200, "Conversation list p95 > 200ms")
def test_conversations_search_latency(self):
- """Search conversations — LIKE across titles, content, peer hashes."""
+ """Search conversations: LIKE across titles, content, peer hashes."""
print("\n[Conversations] Search:")
terms = [
"Message title 5",
@@ -399,7 +399,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
self.assertLess(stats["p95"], 300, "Paginated conversations p95 > 300ms")
# ===================================================================
- # MESSAGES — load, search, upsert (drafts)
+ # MESSAGES: load, search, upsert (drafts)
# ===================================================================
def test_message_load_latency(self):
@@ -422,7 +422,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
self.assertLess(stats["p99"], 50, "Message load p99 > 50ms")
def test_message_search_latency(self):
- """Search messages across all conversations — the global search."""
+ """Search messages across all conversations: the global search."""
print("\n[Messages] Global search:")
terms = [
"Message title 100",
@@ -443,7 +443,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
self.assertLess(stats["p95"], 300, "Message search p95 > 300ms")
def test_message_upsert_throughput(self):
- """Measure message upsert throughput — simulates saving drafts rapidly."""
+ """Measure message upsert throughput: simulates saving drafts rapidly."""
print("\n[Messages] Upsert throughput (draft saves):")
durations = []
peer = secrets.token_hex(16)
@@ -462,7 +462,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
self.assertLess(stats["p95"], 10, "Message upsert p95 > 10ms")
def test_message_upsert_update_throughput(self):
- """Measure message UPDATE throughput — re-saving existing messages (state changes)."""
+ """Measure message UPDATE throughput: re-saving existing messages (state changes)."""
print("\n[Messages] Update existing messages:")
peer = secrets.token_hex(16)
msgs = []
@@ -487,7 +487,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
)
# ===================================================================
- # CONCURRENT WRITERS — contention stress
+ # CONCURRENT WRITERS: contention stress
# ===================================================================
def test_concurrent_message_writers(self):
@@ -584,7 +584,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
)
def test_concurrent_read_write_contention(self):
- """Writers inserting while readers query — simulates real app usage."""
+ """Writers inserting while readers query: simulates real app usage."""
print("\n[Contention] Mixed read/write:")
num_writers = 4
num_readers = 4
@@ -673,7 +673,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
self.assertLess(ms_con, 50, "Contacts LIKE search > 50ms")
# ===================================================================
- # N+1 BATCH OPERATIONS — transaction wrapping regression tests
+ # N+1 BATCH OPERATIONS: transaction wrapping regression tests
# ===================================================================
def test_mark_conversations_as_read_batch(self):
@@ -732,7 +732,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
)
# ===================================================================
- # INDEX VERIFICATION — confirm new indexes are used
+ # INDEX VERIFICATION: confirm new indexes are used
# ===================================================================
def test_indexes_exist(self):
@@ -782,7 +782,7 @@ class TestPerformanceHotPaths(unittest.TestCase):
self.assertLessEqual(cache_size, -8000)
# ===================================================================
- # QUERY PLAN CHECKS — confirm indexes are actually used
+ # QUERY PLAN CHECKS: confirm indexes are actually used
# ===================================================================
def test_query_plan_messages_by_peer(self):
diff --git a/tests/frontend/SettingsPage.config-persistence.test.js b/tests/frontend/SettingsPage.config-persistence.test.js
index a3f4e14e..343904d6 100644
--- a/tests/frontend/SettingsPage.config-persistence.test.js
+++ b/tests/frontend/SettingsPage.config-persistence.test.js
@@ -73,7 +73,7 @@ async function mountSettingsPage(api, router = { push: vi.fn() }) {
return wrapper;
}
-describe("SettingsPage — config persistence (PATCH and related)", () => {
+describe("SettingsPage: config persistence (PATCH and related)", () => {
let serverConfigRef;
let api;
@@ -486,7 +486,7 @@ describe("SettingsPage — config persistence (PATCH and related)", () => {
});
});
-describe("SettingsPage — transport mode (POST, not PATCH)", () => {
+describe("SettingsPage: transport mode (POST, not PATCH)", () => {
let serverConfigRef;
let api;
@@ -516,7 +516,7 @@ describe("SettingsPage — transport mode (POST, not PATCH)", () => {
});
});
-describe("SettingsPage — visualiser display prefs (localStorage + emitter)", () => {
+describe("SettingsPage: visualiser display prefs (localStorage + emitter)", () => {
let serverConfigRef;
let api;
@@ -578,7 +578,7 @@ describe("SettingsPage — visualiser display prefs (localStorage + emitter)", (
});
});
-describe("SettingsPage — maintenance, exports, telemetry trust, RNS reload", () => {
+describe("SettingsPage: maintenance, exports, telemetry trust, RNS reload", () => {
let serverConfigRef;
let api;
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────